mirror of
https://github.com/radzenhq/radzen-blazor.git
synced 2026-02-09 05:35:22 +00:00
Compare commits
347 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fc6fe54635 | ||
|
|
505ffa7fe4 | ||
|
|
4f82ac9599 | ||
|
|
5bbf7c1fde | ||
|
|
e416cede62 | ||
|
|
270a7e0f80 | ||
|
|
1a12a75bde | ||
|
|
d1917eac0c | ||
|
|
09830f0ea2 | ||
|
|
d34e0684fb | ||
|
|
482eca3278 | ||
|
|
5c8ac16c83 | ||
|
|
29382cf0f4 | ||
|
|
53204cc8d6 | ||
|
|
6cf550c517 | ||
|
|
7bf107af4c | ||
|
|
adf2785a5a | ||
|
|
cae44df00a | ||
|
|
8ba1c69573 | ||
|
|
56031c2fd4 | ||
|
|
ad44802d30 | ||
|
|
64ca088e61 | ||
|
|
f4777565a2 | ||
|
|
eb1423e757 | ||
|
|
596b251511 | ||
|
|
e186315935 | ||
|
|
cba9a5120d | ||
|
|
ee62a21ab6 | ||
|
|
0a5e318f80 | ||
|
|
8dd7d7f521 | ||
|
|
69573b2d7d | ||
|
|
5b933c6643 | ||
|
|
126b2d1efa | ||
|
|
a65c1a9482 | ||
|
|
4939e8498a | ||
|
|
e380467853 | ||
|
|
d3adc9733b | ||
|
|
a03fc50ee8 | ||
|
|
c9201bf947 | ||
|
|
8c8d288afd | ||
|
|
1a679af008 | ||
|
|
bc1654a405 | ||
|
|
94ef62e00b | ||
|
|
13afd0fddb | ||
|
|
fa7120571d | ||
|
|
4d4e882dcc | ||
|
|
af8c7179d1 | ||
|
|
c743054184 | ||
|
|
935d850296 | ||
|
|
ad6b6886f1 | ||
|
|
d5d08c89de | ||
|
|
5a246f732e | ||
|
|
669ad421d9 | ||
|
|
98e8393d14 | ||
|
|
de8dfb7e93 | ||
|
|
61b32d6490 | ||
|
|
5057fb1cc8 | ||
|
|
ed49dfc2fa | ||
|
|
839018795c | ||
|
|
c541f9d3e5 | ||
|
|
5e5957e4c4 | ||
|
|
84a759267e | ||
|
|
eb1e6fab0d | ||
|
|
6094953009 | ||
|
|
b845c73ad6 | ||
|
|
2788606105 | ||
|
|
aebf267320 | ||
|
|
33896ea8db | ||
|
|
cb8699f315 | ||
|
|
2a43db6560 | ||
|
|
0ad1200870 | ||
|
|
2debdbfd38 | ||
|
|
07c96bd4bd | ||
|
|
704c6abe7c | ||
|
|
e0e0e608e2 | ||
|
|
40e15a0720 | ||
|
|
d6235fd147 | ||
|
|
c5a3a911e0 | ||
|
|
4e7a037bc5 | ||
|
|
f1c3f46ad7 | ||
|
|
6c0e39da20 | ||
|
|
2a9d638acf | ||
|
|
426e1ba8e8 | ||
|
|
1bb36ef207 | ||
|
|
67cde0fd59 | ||
|
|
0696cd20d5 | ||
|
|
34fce5188f | ||
|
|
273ba0381f | ||
|
|
63a05d86e8 | ||
|
|
283e115d0a | ||
|
|
b513ebba8e | ||
|
|
e324cea7b9 | ||
|
|
c3cda91a2d | ||
|
|
bb9f611629 | ||
|
|
6cc8e62b15 | ||
|
|
5a44b76adb | ||
|
|
109d6bc617 | ||
|
|
7249ff6107 | ||
|
|
406830fa13 | ||
|
|
a6a3278443 | ||
|
|
9fd8531529 | ||
|
|
c687976796 | ||
|
|
ae511929f7 | ||
|
|
e78e756206 | ||
|
|
2e9e0dac1a | ||
|
|
bff97712e2 | ||
|
|
78bc25cce0 | ||
|
|
a2e829417d | ||
|
|
9902413daf | ||
|
|
06a35f4a73 | ||
|
|
e2a46157b9 | ||
|
|
c4913a94c4 | ||
|
|
d6e04c3ae8 | ||
|
|
5b17ef5217 | ||
|
|
8edeedc32c | ||
|
|
543dbc9e50 | ||
|
|
3710f4297b | ||
|
|
91d6ba4da8 | ||
|
|
fbfa3c1abe | ||
|
|
d13d7bef0c | ||
|
|
510f5d7190 | ||
|
|
595b98d4b7 | ||
|
|
c696107aeb | ||
|
|
632722cd7b | ||
|
|
60c264ea49 | ||
|
|
10ce92264d | ||
|
|
2e5c7aa6bc | ||
|
|
1e7e2c5b51 | ||
|
|
46790ce3fe | ||
|
|
7bd891f8f0 | ||
|
|
1ae16acbac | ||
|
|
2281fb7f61 | ||
|
|
89e8db6c6e | ||
|
|
11300692e5 | ||
|
|
a31161c6d8 | ||
|
|
edd85f8ec0 | ||
|
|
0d420604dd | ||
|
|
777f5666e2 | ||
|
|
e7bded641f | ||
|
|
6f0dfbe038 | ||
|
|
0e92eeb50e | ||
|
|
50b6cb879a | ||
|
|
739ebde6a8 | ||
|
|
24ef74f16d | ||
|
|
9e0a57ca5b | ||
|
|
feaebb6f0f | ||
|
|
9a45759414 | ||
|
|
14c5a0447b | ||
|
|
7793e03d82 | ||
|
|
f422573fcc | ||
|
|
516109ecd3 | ||
|
|
31d32b16a5 | ||
|
|
435c1bd6e2 | ||
|
|
6d5ebf3f58 | ||
|
|
b97036b447 | ||
|
|
d7ef3cb896 | ||
|
|
2218eefa67 | ||
|
|
666d33d767 | ||
|
|
3f4f83d354 | ||
|
|
3dea547559 | ||
|
|
0e9c6acb84 | ||
|
|
1b6881673e | ||
|
|
f069b33b60 | ||
|
|
c0a86e31da | ||
|
|
7b95778efe | ||
|
|
ef8a102d0a | ||
|
|
95448de3ed | ||
|
|
0a574762c7 | ||
|
|
59b1440990 | ||
|
|
98d6729d4b | ||
|
|
135a0bbe5c | ||
|
|
c3f579931d | ||
|
|
21c51e81d2 | ||
|
|
e6538c95ad | ||
|
|
0e63e87f9b | ||
|
|
1e900ec775 | ||
|
|
5ff30874dd | ||
|
|
94550006c4 | ||
|
|
202636ce72 | ||
|
|
cae8c6f622 | ||
|
|
0e03c4377f | ||
|
|
7497ea1262 | ||
|
|
d68bb34f6f | ||
|
|
732a6f4942 | ||
|
|
70fb896ae1 | ||
|
|
034eae6722 | ||
|
|
3472949bf0 | ||
|
|
c333b8ca30 | ||
|
|
17e3fbdabf | ||
|
|
30c5c9dfaf | ||
|
|
585d1ee38a | ||
|
|
3255afb487 | ||
|
|
9f75648b50 | ||
|
|
50406f8984 | ||
|
|
f24c7ebc5f | ||
|
|
75dcecfab0 | ||
|
|
34c603ce53 | ||
|
|
7785a73876 | ||
|
|
cfa8f731f2 | ||
|
|
5ecd05c7b3 | ||
|
|
4ec95c2f1c | ||
|
|
9e4413b02e | ||
|
|
38d0d689b3 | ||
|
|
799c5e9e4e | ||
|
|
b98dffda8f | ||
|
|
d816d841a8 | ||
|
|
1730e02dc3 | ||
|
|
53737d2f3c | ||
|
|
932dc3f9f6 | ||
|
|
567fdb6a36 | ||
|
|
3e323b929d | ||
|
|
a53fb126ac | ||
|
|
ec2c17bc21 | ||
|
|
3894f31c2c | ||
|
|
342b96801a | ||
|
|
36f1dfe2e0 | ||
|
|
f90951add1 | ||
|
|
7d65c45833 | ||
|
|
3629235a00 | ||
|
|
09ff7744dc | ||
|
|
1fa9d59634 | ||
|
|
ccec9dbc70 | ||
|
|
a49dbad913 | ||
|
|
4f0497158b | ||
|
|
4757d158a8 | ||
|
|
4bbd371a42 | ||
|
|
10b4b877de | ||
|
|
3bd030c397 | ||
|
|
7c408808c9 | ||
|
|
39dcaa8fc4 | ||
|
|
04cdd584a2 | ||
|
|
2693487f81 | ||
|
|
d73dce8804 | ||
|
|
cf333e8c15 | ||
|
|
5aea28611b | ||
|
|
c58ccf0a66 | ||
|
|
d5c870eb9d | ||
|
|
a41b00e0be | ||
|
|
383ee7e577 | ||
|
|
b3b1f887ef | ||
|
|
b6089f5d28 | ||
|
|
2d7d5cad39 | ||
|
|
cb06833882 | ||
|
|
cf3ab86cec | ||
|
|
efd3d18484 | ||
|
|
e6baaa6184 | ||
|
|
81c943ecda | ||
|
|
587a4f479c | ||
|
|
5638152121 | ||
|
|
81fc744270 | ||
|
|
2437b9e028 | ||
|
|
d148cac10e | ||
|
|
af8c9e3cc3 | ||
|
|
b64c71c307 | ||
|
|
2d60d8fcc1 | ||
|
|
814221997e | ||
|
|
5e112b6f59 | ||
|
|
6b253c30b2 | ||
|
|
b5b0c1efa7 | ||
|
|
5dce6f81af | ||
|
|
6ff186d7de | ||
|
|
801a1cab1a | ||
|
|
737539e3a8 | ||
|
|
2992f868ef | ||
|
|
e718ef80dc | ||
|
|
220a441422 | ||
|
|
8440eb513a | ||
|
|
09004fd7db | ||
|
|
e9fb2bf677 | ||
|
|
81261e2413 | ||
|
|
2c7b51dc00 | ||
|
|
47447badc1 | ||
|
|
1233a2347d | ||
|
|
b0cec6f668 | ||
|
|
05e25d51ec | ||
|
|
81c27836c2 | ||
|
|
24aa1ec1b3 | ||
|
|
6c7bc84ff4 | ||
|
|
bc1d139cca | ||
|
|
c1afef18f3 | ||
|
|
f46ff6237b | ||
|
|
fd94e8037d | ||
|
|
b95bf75dce | ||
|
|
b08b3aa3b1 | ||
|
|
5ed931243e | ||
|
|
f5ab48c8af | ||
|
|
75202f4ff8 | ||
|
|
3c679674d5 | ||
|
|
6f7921691c | ||
|
|
13ba136c66 | ||
|
|
abd80af816 | ||
|
|
aac927e6ea | ||
|
|
f7e0d7d698 | ||
|
|
eb633bae3a | ||
|
|
76a0b5e3e2 | ||
|
|
0679525905 | ||
|
|
3396c7bca4 | ||
|
|
5031ce3936 | ||
|
|
8b774b47e2 | ||
|
|
f0e7a398fa | ||
|
|
72c3f23c67 | ||
|
|
33c01b611e | ||
|
|
7cc2e6953b | ||
|
|
1284d4ab80 | ||
|
|
cd83f01132 | ||
|
|
3b4480fa8b | ||
|
|
a9f8fbbff5 | ||
|
|
e37b055e24 | ||
|
|
8eeda0e647 | ||
|
|
c28b47e231 | ||
|
|
ab989107a5 | ||
|
|
268fe93dd9 | ||
|
|
e154fb05ea | ||
|
|
fb14fe587b | ||
|
|
e72a17ce32 | ||
|
|
6c11409e7e | ||
|
|
f43db71b01 | ||
|
|
dc3611474f | ||
|
|
5c5a306f7f | ||
|
|
024d9fb5ac | ||
|
|
6fb480042f | ||
|
|
acc1f8624e | ||
|
|
0b97853a57 | ||
|
|
8408b55e08 | ||
|
|
e49d9f7eab | ||
|
|
6202bd78f3 | ||
|
|
a2a5916e09 | ||
|
|
3f52e42661 | ||
|
|
224032d0c2 | ||
|
|
4dece2e58a | ||
|
|
7eb85d4454 | ||
|
|
bd750e6f97 | ||
|
|
ee0d4d29e1 | ||
|
|
10d2ea7c0a | ||
|
|
0ab76d53ec | ||
|
|
3dea0a5f67 | ||
|
|
41c2e04cfa | ||
|
|
d8183feb07 | ||
|
|
e49fe2bca8 | ||
|
|
67664a2816 | ||
|
|
eefbe00aec | ||
|
|
be67d5f120 | ||
|
|
fa1fe694cf | ||
|
|
9f681c0c09 | ||
|
|
18dde2849e | ||
|
|
5678774de0 | ||
|
|
f5370e1384 |
11
.github/ISSUE_TEMPLATE/bug_report.md
vendored
11
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -11,10 +11,10 @@ assignees: ''
|
||||
|
||||
IMPORTANT: Read this first!!!
|
||||
|
||||
1. If you own a Radzen Professional or Еnterprise subscription you can report your issue or ask us a question via email at info@radzen.com. Radzen staff will reply within 24 hours (Professional) or 16 hours (Enterprise)
|
||||
1. If you own a Radzen Blazor Pro or Team subscription you can also report your issue or ask us a question via email at info@radzen.com. Radzen staff will reply within 24 hours (Pro) or 16 hours (Team)
|
||||
2. The Radzen staff guarantees a response to issues in this repo only to paid subscribers.
|
||||
3. If you have a HOW TO question start a new forum thread in the Radzen Community forum: https://forum.radzen.com. Radzen staff will close issues that are HOWTO questions.
|
||||
4. Please adhere to the issue template. Specify all the steps required to reproduce the issue or link a project which reproduces it easily (without requiring extra steps such as restoring a database).
|
||||
4. Please adhere to the issue template. Specify all the steps required to reproduce the issue.
|
||||
-->
|
||||
|
||||
**Describe the bug**
|
||||
@@ -27,7 +27,12 @@ Steps to reproduce the behavior:
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
Alternatively link your repo with a sample project that can be run.
|
||||
Alternatively make a new [playground](https://blazor.radzen.com/playground) snippet and paste its URL.
|
||||
1. Go to any live demo at https://blazor.radzen.com
|
||||
2. Click the **Edit Source** tab.
|
||||
3. Then click **Open in Playground**.
|
||||
4. Reproduce the problem and save the snippet.
|
||||
5. Copy the snippet URL and provide it in the issue description.
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/config.yml
vendored
2
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -5,4 +5,4 @@ contact_links:
|
||||
about: Please ask and answer questions here.
|
||||
- name: Radzen Commercial Support
|
||||
url: info@radzen.com
|
||||
about: Radzen Professional or Enterprise subscribers can get dedicated support over email.
|
||||
about: Radzen Blazor subscribers can get dedicated support over email.
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/feature_request.md
vendored
2
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -11,7 +11,7 @@ assignees: ''
|
||||
|
||||
IMPORTANT: Read this first!!!
|
||||
|
||||
1. If you own a Radzen Professional or Еnterprise subscription you can request your feature via email at info@radzen.com. Radzen staff will reply within 24 hours (Professional) or 16 hours (Enterprise)
|
||||
1. If you own a Radzen Blazor subscription you can request your feature via email at info@radzen.com. Radzen staff will reply within 24 hours (Pro) or 16 hours (Team)
|
||||
2. The Radzen staff guarantees a response to issues in this repo only to paid subscribers.
|
||||
3. If you have a HOW TO question start a new forum thread in the Radzen Community forum: https://forum.radzen.com. Radzen staff will close issues that are HOWTO questions.
|
||||
4. Please adhere to the issue template.
|
||||
|
||||
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@@ -20,7 +20,7 @@ jobs:
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: 9.0.x
|
||||
dotnet-version: 10.0.x
|
||||
|
||||
- name: Build
|
||||
run: dotnet build Radzen.Blazor/Radzen.Blazor.csproj
|
||||
|
||||
66
.github/workflows/deploy.yml
vendored
Normal file
66
.github/workflows/deploy.yml
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
name: Deploy to blazor.radzen.com
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: blazor-radzen-prod
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build-and-deploy:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME: ${{ github.repository }}
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Compute image tag (timestamp)
|
||||
id: meta
|
||||
run: |
|
||||
TAG=$(date -u +"%Y%m%d%H%M%S")
|
||||
IMAGE="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${TAG}"
|
||||
echo "image=$IMAGE" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Log in to GHCR
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.image }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
- name: Push to dokku
|
||||
uses: dokku/github-action@master
|
||||
with:
|
||||
git_remote_url: ${{ secrets.DOKKU_REPO }}
|
||||
ssh_private_key: ${{ secrets.DOKKU_SSH_PRIVATE_KEY }}
|
||||
deploy_docker_image: ${{ steps.meta.outputs.image }}
|
||||
|
||||
- name: Prune GHCR versions (keep 1)
|
||||
uses: actions/delete-package-versions@v5
|
||||
with:
|
||||
package-name: radzen-blazor
|
||||
package-type: container
|
||||
min-versions-to-keep: 1
|
||||
delete-only-pre-release-versions: false
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -347,3 +347,4 @@ Radzen.Blazor.min.js
|
||||
*.md
|
||||
/.gitignore
|
||||
/.gitignore
|
||||
RadzenBlazorDemos/wwwroot/demos
|
||||
|
||||
@@ -19,7 +19,7 @@ You can ask your question here. Please use the [Radzen.Blazor Components](https:
|
||||
|
||||
### Dedicated technical support
|
||||
|
||||
Radzen staff provides technical support with guaranteed response time to Radzen Professional and Enterprise subscribers. The pricing options are available [here](https://www.radzen.com/blazor-studio/pricing/).
|
||||
Radzen staff provides technical support with guaranteed response time to Radzen Professional and Enterprise subscribers. The pricing options are available [here](https://www.radzen.com/pricing).
|
||||
|
||||
## How Can I Contribute?
|
||||
|
||||
|
||||
85
Directory.Build.props
Normal file
85
Directory.Build.props
Normal file
@@ -0,0 +1,85 @@
|
||||
<Project>
|
||||
<!--
|
||||
Common build properties for all projects in the Radzen.Blazor solution.
|
||||
|
||||
To use this file:
|
||||
1. Rename to Directory.Build.props (remove .sample extension)
|
||||
2. Adjust settings based on your needs
|
||||
3. Review the analyzer settings in .editorconfig
|
||||
|
||||
This file will be automatically imported by all projects in subdirectories.
|
||||
-->
|
||||
|
||||
<PropertyGroup Label="Language Configuration">
|
||||
<!-- Use latest C# language features -->
|
||||
<LangVersion>latest</LangVersion>
|
||||
|
||||
<!-- Do NOT enable implicit usings - explicit imports preferred for library code -->
|
||||
<ImplicitUsings>disable</ImplicitUsings>
|
||||
|
||||
<Nullable>enable</Nullable>
|
||||
<NoWarn>CA2007</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Label="Code Analysis Configuration">
|
||||
<!-- Enable .NET code analyzers -->
|
||||
<AnalysisLevel>latest</AnalysisLevel>
|
||||
<EnableNETAnalyzers>true</EnableNETAnalyzers>
|
||||
|
||||
<!-- Run analyzers during build and in IDE -->
|
||||
<RunAnalyzersDuringBuild>true</RunAnalyzersDuringBuild>
|
||||
<RunAnalyzersDuringLiveAnalysis>true</RunAnalyzersDuringLiveAnalysis>
|
||||
|
||||
<!-- Don't enforce code style in build (yet) - just show warnings -->
|
||||
<EnforceCodeStyleInBuild>false</EnforceCodeStyleInBuild>
|
||||
|
||||
<!-- Don't treat warnings as errors (yet) - too many to fix immediately -->
|
||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
||||
|
||||
<!-- Report all analyzer diagnostics -->
|
||||
<AnalysisMode>All</AnalysisMode>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Label="Build Quality">
|
||||
<!-- Enable deterministic builds for reproducibility -->
|
||||
<Deterministic>true</Deterministic>
|
||||
|
||||
<!-- Enable deterministic builds in CI/CD -->
|
||||
<ContinuousIntegrationBuild Condition="'$(CI)' == 'true'">true</ContinuousIntegrationBuild>
|
||||
|
||||
<!-- Embed source files for better debugging -->
|
||||
<EmbedAllSources>true</EmbedAllSources>
|
||||
<!--
|
||||
IMPORTANT:
|
||||
- NuGet symbol packages (.snupkg) require portable PDB files.
|
||||
- If DebugType=embedded, there are no standalone PDBs, so the .snupkg ends up effectively empty.
|
||||
Use portable PDBs when symbols are enabled; otherwise use embedded for local debugging convenience.
|
||||
-->
|
||||
<!--
|
||||
NOTE: Directory.Build.props is imported before project files, so properties like IncludeSymbols
|
||||
set in a .csproj may not be available yet for Conditions here.
|
||||
IsPacking *is* set by `dotnet pack`, so use that to switch DebugType for symbol packages.
|
||||
-->
|
||||
<DebugType Condition="'$(IsPacking)' == 'true'">portable</DebugType>
|
||||
<DebugType Condition="'$(IsPacking)' != 'true'">embedded</DebugType>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Label="Demos and Tests Project Configuration" Condition="$(MSBuildProjectName.Contains('Demos')) OR $(MSBuildProjectName.Contains('Tests'))">
|
||||
<!-- Demo projects and Tests should not be packable -->
|
||||
<IsPackable>false</IsPackable>
|
||||
|
||||
<!-- DISABLE ALL ANALYZERS FOR DEMO PROJECTS AND TESTS -->
|
||||
<EnableNETAnalyzers>false</EnableNETAnalyzers>
|
||||
<RunAnalyzersDuringBuild>false</RunAnalyzersDuringBuild>
|
||||
<RunAnalyzersDuringLiveAnalysis>false</RunAnalyzersDuringLiveAnalysis>
|
||||
<EnforceCodeStyleInBuild>false</EnforceCodeStyleInBuild>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Label="Performance">
|
||||
<!-- Optimize startup time -->
|
||||
<TieredCompilation>true</TieredCompilation>
|
||||
<TieredCompilationQuickJit>true</TieredCompilationQuickJit>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
50
Dockerfile
50
Dockerfile
@@ -1,24 +1,48 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
FROM mcr.microsoft.com/dotnet/sdk:9.0
|
||||
# =============================
|
||||
# BUILD STAGE
|
||||
# =============================
|
||||
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
|
||||
WORKDIR /src
|
||||
|
||||
COPY Radzen.Blazor /app/Radzen.Blazor
|
||||
COPY Radzen.DocFX /app/Radzen.DocFX
|
||||
COPY RadzenBlazorDemos /app/RadzenBlazorDemos
|
||||
COPY RadzenBlazorDemos.Host /app/RadzenBlazorDemos.Host
|
||||
# Copy project files first for better caching
|
||||
COPY Radzen.Blazor/*.csproj Radzen.Blazor/
|
||||
COPY RadzenBlazorDemos/*.csproj RadzenBlazorDemos/
|
||||
COPY RadzenBlazorDemos.Host/*.csproj RadzenBlazorDemos.Host/
|
||||
|
||||
WORKDIR /app
|
||||
# Radzen.DocFX usually has no csproj → copy full folder
|
||||
COPY Radzen.DocFX/ Radzen.DocFX/
|
||||
|
||||
# Restore dependencies
|
||||
RUN dotnet restore RadzenBlazorDemos.Host/RadzenBlazorDemos.Host.csproj
|
||||
|
||||
# Copy full source after restore layer
|
||||
COPY . .
|
||||
|
||||
# Install docfx (build stage only)
|
||||
RUN dotnet tool install -g docfx
|
||||
ENV PATH="$PATH:/root/.dotnet/tools"
|
||||
RUN wget https://dot.net/v1/dotnet-install.sh \
|
||||
&& bash dotnet-install.sh --channel 8.0 --runtime dotnet --install-dir /usr/share/dotnet
|
||||
|
||||
# Build shared project (keep net8.0 if required)
|
||||
RUN dotnet build -c Release Radzen.Blazor/Radzen.Blazor.csproj -f net8.0
|
||||
|
||||
# Generate documentation
|
||||
RUN docfx Radzen.DocFX/docfx.json
|
||||
|
||||
WORKDIR /app/RadzenBlazorDemos.Host
|
||||
RUN dotnet publish -c Release -o out
|
||||
# Publish the Blazor host app
|
||||
WORKDIR /src/RadzenBlazorDemos.Host
|
||||
RUN dotnet publish -c Release -o /app/out
|
||||
|
||||
ENV ASPNETCORE_URLS=http://*:5000
|
||||
WORKDIR /app/RadzenBlazorDemos.Host/out
|
||||
|
||||
# =============================
|
||||
# RUNTIME STAGE
|
||||
# =============================
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS runtime
|
||||
WORKDIR /app
|
||||
|
||||
# Copy only published output
|
||||
COPY --from=build /app/out ./
|
||||
|
||||
# Set runtime URL
|
||||
ENV ASPNETCORE_URLS=http://+:5000
|
||||
|
||||
ENTRYPOINT ["dotnet", "RadzenBlazorDemos.Host.dll"]
|
||||
|
||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2018-2025 Radzen Ltd
|
||||
Copyright (c) 2018-2026 Radzen Ltd
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
12
README.md
12
README.md
@@ -3,7 +3,7 @@
|
||||
Radzen Blazor Components
|
||||
========================
|
||||
|
||||
A set of **90+ free and open source** native Blazor UI controls.
|
||||
The most sophisticated free UI component library for Blazor, featuring **100+ native components**.
|
||||
|
||||
See Online Demos or Read the Docs
|
||||
|
||||
@@ -15,7 +15,7 @@ See Online Demos or Read the Docs
|
||||
|
||||
Radzen Blazor Components are open source and free for commercial use. You can install them from [NuGet](https://www.nuget.org/packages/Radzen.Blazor) or build your own copy from source.
|
||||
|
||||
Paid support is available as part of the [Radzen Professional subscription](https://www.radzen.com/blazor-studio/pricing/).
|
||||
Paid support is available as part of the [Radzen Blazor subscription](https://www.radzen.com/pricing).
|
||||
|
||||
### :computer: Native
|
||||
|
||||
@@ -38,9 +38,9 @@ Everybody is welcome to visit the [Radzen Community forum](https://forum.radzen.
|
||||
|
||||
The Radzen team monitors the forum threads, but does not guarantee a response to every question. For guaranteed responses you may consider the dedicated support option.
|
||||
|
||||
Dedicated support for the Radzen Blazor Components is available as part of the [Radzen Professional subscription](https://www.radzen.com/blazor-studio/pricing/).
|
||||
Dedicated support for the Radzen Blazor Components is available as part of the [Radzen Blazor subscription](https://www.radzen.com/pricing).
|
||||
|
||||
Our flagship product [Radzen Blazor Studio](https://www.radzen.com/blazor-studio/) provides tons of productivity features for Blazor developers:
|
||||
Our flagship product [Radzen Blazor Studio](https://www.radzen.com/blazor-studio) provides tons of productivity features for Blazor developers:
|
||||
- An industry-leading WYSIWYG Blazor design time canvas
|
||||
- Scaffolding a complete CRUD applications from a database
|
||||
- Built-in security - authentication and authorization
|
||||
@@ -50,8 +50,8 @@ Our flagship product [Radzen Blazor Studio](https://www.radzen.com/blazor-studio
|
||||
|
||||
## Get started with Radzen Blazor Components
|
||||
|
||||
Check the [getting started](https://blazor.radzen.com/getting-started) instructions to start making awesome Blazor applications.
|
||||
Check the [getting started](https://blazor.radzen.com/get-started) instructions to start making awesome Blazor applications.
|
||||
|
||||
## Run demos locally
|
||||
|
||||
Use Radzen.Server.sln to open and run demos as Blazor server application or Radzen.WebAssembly.sln to open and run demos as Blazor WebAssembly application. Radzen.sln has reference to all projects including tests.
|
||||
Use **Radzen.Server.sln** to open and run demos as Blazor server application or **Radzen.WebAssembly.sln** to open and run demos as Blazor WebAssembly application. The demos require the .NET 10 SDK and should preferably be opened in VS2026.
|
||||
|
||||
195
Radzen.Blazor.Tests/AccordionTests.cs
Normal file
195
Radzen.Blazor.Tests/AccordionTests.cs
Normal file
@@ -0,0 +1,195 @@
|
||||
using Bunit;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class AccordionTests
|
||||
{
|
||||
[Fact]
|
||||
public void Accordion_Renders_CssClasses()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenAccordion>();
|
||||
|
||||
Assert.Contains(@"rz-accordion", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Accordion_Renders_AccordionItems()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenAccordion>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Items, builder =>
|
||||
{
|
||||
builder.OpenComponent<RadzenAccordionItem>(0);
|
||||
builder.AddAttribute(1, "Text", "Test Item");
|
||||
builder.AddAttribute(2, "ChildContent", (RenderFragment)(contentBuilder =>
|
||||
{
|
||||
contentBuilder.AddContent(0, "Item Content");
|
||||
}));
|
||||
builder.CloseComponent();
|
||||
});
|
||||
});
|
||||
|
||||
Assert.Contains("Test Item", component.Markup);
|
||||
Assert.Contains("Item Content", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Accordion_Renders_ItemWithIcon()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenAccordion>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Items, builder =>
|
||||
{
|
||||
builder.OpenComponent<RadzenAccordionItem>(0);
|
||||
builder.AddAttribute(1, "Text", "Orders");
|
||||
builder.AddAttribute(2, "Icon", "account_balance_wallet");
|
||||
builder.AddAttribute(3, "ChildContent", (RenderFragment)(contentBuilder =>
|
||||
{
|
||||
contentBuilder.AddContent(0, "Order Details");
|
||||
}));
|
||||
builder.CloseComponent();
|
||||
});
|
||||
});
|
||||
|
||||
Assert.Contains("account_balance_wallet", component.Markup);
|
||||
Assert.Contains("Orders", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Accordion_SingleExpand_OnlyOneItemExpanded()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenAccordion>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Multiple, false); // Single expand mode
|
||||
parameters.Add(p => p.Items, builder =>
|
||||
{
|
||||
// Add first item
|
||||
builder.OpenComponent<RadzenAccordionItem>(0);
|
||||
builder.AddAttribute(1, "Text", "Item 1");
|
||||
builder.AddAttribute(2, "ChildContent", (RenderFragment)(b => b.AddContent(0, "Content 1")));
|
||||
builder.CloseComponent();
|
||||
|
||||
// Add second item
|
||||
builder.OpenComponent<RadzenAccordionItem>(1);
|
||||
builder.AddAttribute(1, "Text", "Item 2");
|
||||
builder.AddAttribute(2, "ChildContent", (RenderFragment)(b => b.AddContent(0, "Content 2")));
|
||||
builder.CloseComponent();
|
||||
});
|
||||
});
|
||||
|
||||
Assert.False(component.Instance.Multiple);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Accordion_MultipleExpand_AllowsMultipleItemsExpanded()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenAccordion>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Multiple, true);
|
||||
});
|
||||
|
||||
Assert.True(component.Instance.Multiple);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Accordion_Raises_ExpandEvent()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
|
||||
var expandRaised = false;
|
||||
int expandedIndex = -1;
|
||||
|
||||
var component = ctx.RenderComponent<RadzenAccordion>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Expand, EventCallback.Factory.Create<int>(this, (index) =>
|
||||
{
|
||||
expandRaised = true;
|
||||
expandedIndex = index;
|
||||
}));
|
||||
parameters.Add(p => p.Items, builder =>
|
||||
{
|
||||
builder.OpenComponent<RadzenAccordionItem>(0);
|
||||
builder.AddAttribute(1, "Text", "Test Item");
|
||||
builder.AddAttribute(2, "ChildContent", (RenderFragment)(b => b.AddContent(0, "Content")));
|
||||
builder.CloseComponent();
|
||||
});
|
||||
});
|
||||
|
||||
// Find and click the accordion header link to expand
|
||||
var header = component.Find(".rz-accordion-header a");
|
||||
header.Click();
|
||||
|
||||
Assert.True(expandRaised);
|
||||
Assert.Equal(0, expandedIndex);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Accordion_Raises_CollapseEvent()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
|
||||
var collapseRaised = false;
|
||||
int collapsedIndex = -1;
|
||||
|
||||
var component = ctx.RenderComponent<RadzenAccordion>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Collapse, EventCallback.Factory.Create<int>(this, (index) =>
|
||||
{
|
||||
collapseRaised = true;
|
||||
collapsedIndex = index;
|
||||
}));
|
||||
parameters.Add(p => p.Items, builder =>
|
||||
{
|
||||
builder.OpenComponent<RadzenAccordionItem>(0);
|
||||
builder.AddAttribute(1, "Text", "Test Item");
|
||||
builder.AddAttribute(2, "Selected", true); // Start expanded
|
||||
builder.AddAttribute(3, "ChildContent", (RenderFragment)(b => b.AddContent(0, "Content")));
|
||||
builder.CloseComponent();
|
||||
});
|
||||
});
|
||||
|
||||
// Find and click the accordion header link to collapse
|
||||
var header = component.Find(".rz-accordion-header a");
|
||||
header.Click();
|
||||
|
||||
Assert.True(collapseRaised);
|
||||
Assert.Equal(0, collapsedIndex);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Accordion_DisabledItem_CannotExpand()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
|
||||
var expandRaised = false;
|
||||
|
||||
var component = ctx.RenderComponent<RadzenAccordion>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Expand, EventCallback.Factory.Create<int>(this, (_) => expandRaised = true));
|
||||
parameters.Add(p => p.Items, builder =>
|
||||
{
|
||||
builder.OpenComponent<RadzenAccordionItem>(0);
|
||||
builder.AddAttribute(1, "Text", "Disabled Item");
|
||||
builder.AddAttribute(2, "Disabled", true);
|
||||
builder.AddAttribute(3, "ChildContent", (RenderFragment)(b => b.AddContent(0, "Content")));
|
||||
builder.CloseComponent();
|
||||
});
|
||||
});
|
||||
|
||||
// Try to click the disabled item
|
||||
var header = component.Find(".rz-accordion-header a");
|
||||
header.Click();
|
||||
|
||||
// Event should not be raised for disabled item
|
||||
Assert.False(expandRaised);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
195
Radzen.Blazor.Tests/AlertTests.cs
Normal file
195
Radzen.Blazor.Tests/AlertTests.cs
Normal file
@@ -0,0 +1,195 @@
|
||||
using Bunit;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class AlertTests
|
||||
{
|
||||
[Fact]
|
||||
public void Alert_Renders_CssClasses()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenAlert>();
|
||||
|
||||
Assert.Contains(@"rz-alert", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Alert_Renders_AlertStyle()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenAlert>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.AlertStyle, AlertStyle.Danger));
|
||||
Assert.Contains("rz-danger", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.AlertStyle, AlertStyle.Success));
|
||||
Assert.Contains("rz-success", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.AlertStyle, AlertStyle.Warning));
|
||||
Assert.Contains("rz-warning", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.AlertStyle, AlertStyle.Info));
|
||||
Assert.Contains("rz-info", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Alert_Renders_Shade()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenAlert>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters
|
||||
.Add(p => p.AlertStyle, AlertStyle.Primary)
|
||||
.Add(p => p.Shade, Shade.Lighter));
|
||||
|
||||
Assert.Contains("rz-shade-lighter", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters
|
||||
.Add(p => p.AlertStyle, AlertStyle.Primary)
|
||||
.Add(p => p.Shade, Shade.Darker));
|
||||
|
||||
Assert.Contains("rz-shade-darker", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Alert_Renders_Variant()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenAlert>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Variant, Variant.Outlined));
|
||||
Assert.Contains("rz-variant-outlined", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Variant, Variant.Flat));
|
||||
Assert.Contains("rz-variant-flat", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Alert_Renders_Title()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenAlert>();
|
||||
|
||||
var title = "Alert Title";
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Title, title));
|
||||
|
||||
Assert.Contains(title, component.Markup);
|
||||
Assert.Contains("rz-alert-title", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Alert_Renders_Text()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenAlert>();
|
||||
|
||||
var text = "This is an alert message";
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Text, text));
|
||||
|
||||
Assert.Contains(text, component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Alert_Renders_ChildContent()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenAlert>(parameters =>
|
||||
{
|
||||
parameters.AddChildContent("Custom alert content");
|
||||
});
|
||||
|
||||
Assert.Contains("Custom alert content", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Alert_ShowIcon_DisplaysIcon()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenAlert>();
|
||||
|
||||
// Default should show icon
|
||||
Assert.Contains("rz-alert-icon", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.ShowIcon, false));
|
||||
Assert.DoesNotContain("rz-alert-icon", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Alert_AllowClose_DisplaysCloseButton()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenAlert>();
|
||||
|
||||
// Default AllowClose is true - should contain a button with close icon
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.AllowClose, true));
|
||||
Assert.Contains("close", component.Markup);
|
||||
Assert.Contains("rz-button", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.AllowClose, false));
|
||||
// When AllowClose is false, should not have close button
|
||||
var buttonCount = System.Text.RegularExpressions.Regex.Matches(component.Markup, "rz-button").Count;
|
||||
Assert.Equal(0, buttonCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Alert_CloseButton_RaisesCloseEvent()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
|
||||
var closeRaised = false;
|
||||
|
||||
var component = ctx.RenderComponent<RadzenAlert>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowClose, true);
|
||||
parameters.Add(p => p.Close, () => closeRaised = true);
|
||||
});
|
||||
|
||||
var closeButton = component.Find("button.rz-button");
|
||||
closeButton.Click();
|
||||
|
||||
Assert.True(closeRaised);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Alert_Visible_ControlsDisplay()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
|
||||
var component = ctx.RenderComponent<RadzenAlert>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Visible, true);
|
||||
parameters.Add(p => p.Text, "Visible Alert");
|
||||
});
|
||||
|
||||
Assert.Contains("Visible Alert", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Visible, false));
|
||||
|
||||
// When not visible, component should not render
|
||||
Assert.DoesNotContain("Visible Alert", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Alert_CloseButton_SetsVisibleToFalse()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
|
||||
var visibleValue = true;
|
||||
|
||||
var component = ctx.RenderComponent<RadzenAlert>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Visible, visibleValue);
|
||||
parameters.Add(p => p.AllowClose, true);
|
||||
parameters.Add(p => p.VisibleChanged, (bool value) => visibleValue = value);
|
||||
});
|
||||
|
||||
var closeButton = component.Find("button.rz-button");
|
||||
closeButton.Click();
|
||||
|
||||
Assert.False(visibleValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,77 @@
|
||||
using Xunit;
|
||||
using System.Collections;
|
||||
using Bunit;
|
||||
using Xunit;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class AutoCompleteTests
|
||||
{
|
||||
[Fact]
|
||||
public void AutoComplete_Renders_WithClassName()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenAutoComplete>();
|
||||
|
||||
Assert.Contains(@"rz-autocomplete", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AutoComplete_Renders_InputElement()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenAutoComplete>();
|
||||
|
||||
Assert.Contains("type=\"text\"", component.Markup);
|
||||
Assert.Contains("rz-inputtext", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AutoComplete_Renders_Disabled()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenAutoComplete>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Disabled, true);
|
||||
});
|
||||
|
||||
Assert.Contains("disabled", component.Markup);
|
||||
Assert.Contains("rz-state-disabled", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AutoComplete_Renders_Placeholder()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenAutoComplete>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Placeholder, "Type to search...");
|
||||
});
|
||||
|
||||
Assert.Contains("placeholder=\"Type to search...\"", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AutoComplete_Renders_WithData()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var data = new List<string> { "Apple", "Banana", "Cherry" };
|
||||
|
||||
var component = ctx.RenderComponent<RadzenAutoComplete>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Data, data);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-autocomplete-panel", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AutoComplete_Enum_Converts_To_Attr_Value()
|
||||
{
|
||||
@@ -70,5 +138,30 @@ namespace Radzen.Blazor.Tests
|
||||
Assert.Equal("additional-name", AutoCompleteType.MiddleName.GetAutoCompleteValue());
|
||||
Assert.Equal("family-name", AutoCompleteType.LastName.GetAutoCompleteValue());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AutoComplete_Filters_StringList()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var data = new List<string> { "Apple", "Banana", "Cherry" };
|
||||
|
||||
var component = ctx.RenderComponent<AutoCompleteWithAccessibleView>(parameters =>
|
||||
{
|
||||
parameters
|
||||
.Add(p => p.Data, data)
|
||||
.Add(p => p.SearchText, "Ban")
|
||||
.Add(p => p.OpenOnFocus, true);
|
||||
});
|
||||
|
||||
Assert.Contains("Banana", component.Instance.CurrentView.OfType<string>());
|
||||
Assert.DoesNotContain("Apple", component.Instance.CurrentView.OfType<string>());
|
||||
Assert.DoesNotContain("Cherry", component.Instance.CurrentView.OfType<string>());
|
||||
}
|
||||
|
||||
private sealed class AutoCompleteWithAccessibleView : RadzenAutoComplete
|
||||
{
|
||||
public IEnumerable CurrentView => View;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
46
Radzen.Blazor.Tests/CardTests.cs
Normal file
46
Radzen.Blazor.Tests/CardTests.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
using Bunit;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class CardTests
|
||||
{
|
||||
[Fact]
|
||||
public void Card_Renders_WithClassName()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenCard>();
|
||||
|
||||
Assert.Contains(@"rz-card", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Card_Renders_ChildContent()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenCard>(parameters =>
|
||||
{
|
||||
parameters.AddChildContent("<div>Card Content</div>");
|
||||
});
|
||||
|
||||
Assert.Contains("Card Content", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Card_Renders_Variant()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenCard>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Variant, Variant.Outlined));
|
||||
Assert.Contains("rz-variant-outlined", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Variant, Variant.Filled));
|
||||
Assert.Contains("rz-variant-filled", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Variant, Variant.Flat));
|
||||
Assert.Contains("rz-variant-flat", component.Markup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
173
Radzen.Blazor.Tests/CarouselTests.cs
Normal file
173
Radzen.Blazor.Tests/CarouselTests.cs
Normal file
@@ -0,0 +1,173 @@
|
||||
using Bunit;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class CarouselTests
|
||||
{
|
||||
[Fact]
|
||||
public void Carousel_Renders_WithClassName()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenCarousel>();
|
||||
|
||||
Assert.Contains(@"rz-carousel", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Carousel_Renders_AllowPaging_True()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenCarousel>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowPaging, true);
|
||||
parameters.Add(p => p.Items, builder =>
|
||||
{
|
||||
builder.OpenComponent<RadzenCarouselItem>(0);
|
||||
builder.AddAttribute(1, "ChildContent", (RenderFragment)(b => b.AddContent(0, "Slide 1")));
|
||||
builder.CloseComponent();
|
||||
|
||||
builder.OpenComponent<RadzenCarouselItem>(2);
|
||||
builder.AddAttribute(3, "ChildContent", (RenderFragment)(b => b.AddContent(0, "Slide 2")));
|
||||
builder.CloseComponent();
|
||||
});
|
||||
});
|
||||
|
||||
Assert.Contains("rz-carousel-pager-button", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Carousel_Renders_AllowPaging_False()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenCarousel>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowPaging, false);
|
||||
parameters.Add(p => p.Items, builder =>
|
||||
{
|
||||
builder.OpenComponent<RadzenCarouselItem>(0);
|
||||
builder.AddAttribute(1, "ChildContent", (RenderFragment)(b => b.AddContent(0, "Slide 1")));
|
||||
builder.CloseComponent();
|
||||
});
|
||||
});
|
||||
|
||||
Assert.DoesNotContain("rz-carousel-pager-button", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Carousel_Renders_AllowNavigation_True()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenCarousel>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowNavigation, true);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-carousel-prev", component.Markup);
|
||||
Assert.Contains("rz-carousel-next", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Carousel_Renders_AllowNavigation_False()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenCarousel>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowNavigation, false);
|
||||
});
|
||||
|
||||
Assert.DoesNotContain("rz-carousel-prev", component.Markup);
|
||||
Assert.DoesNotContain("rz-carousel-next", component.Markup);
|
||||
Assert.Contains("rz-carousel-no-navigation", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Carousel_Renders_PagerPosition_Top()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenCarousel>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.PagerPosition, PagerPosition.Top);
|
||||
parameters.Add(p => p.AllowPaging, true);
|
||||
parameters.Add(p => p.Items, builder =>
|
||||
{
|
||||
builder.OpenComponent<RadzenCarouselItem>(0);
|
||||
builder.AddAttribute(1, "ChildContent", (RenderFragment)(b => b.AddContent(0, "Slide")));
|
||||
builder.CloseComponent();
|
||||
});
|
||||
});
|
||||
|
||||
Assert.Contains("rz-carousel-pager-top", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Carousel_Renders_PagerPosition_Bottom()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenCarousel>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.PagerPosition, PagerPosition.Bottom);
|
||||
parameters.Add(p => p.AllowPaging, true);
|
||||
parameters.Add(p => p.Items, builder =>
|
||||
{
|
||||
builder.OpenComponent<RadzenCarouselItem>(0);
|
||||
builder.AddAttribute(1, "ChildContent", (RenderFragment)(b => b.AddContent(0, "Slide")));
|
||||
builder.CloseComponent();
|
||||
});
|
||||
});
|
||||
|
||||
Assert.Contains("rz-carousel-pager-bottom", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Carousel_Renders_PagerPosition_TopAndBottom()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenCarousel>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.PagerPosition, PagerPosition.TopAndBottom);
|
||||
parameters.Add(p => p.AllowPaging, true);
|
||||
parameters.Add(p => p.Items, builder =>
|
||||
{
|
||||
builder.OpenComponent<RadzenCarouselItem>(0);
|
||||
builder.AddAttribute(1, "ChildContent", (RenderFragment)(b => b.AddContent(0, "Slide")));
|
||||
builder.CloseComponent();
|
||||
});
|
||||
});
|
||||
|
||||
Assert.Contains("rz-carousel-pager-top", component.Markup);
|
||||
Assert.Contains("rz-carousel-pager-bottom", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Carousel_Renders_PagerOverlay_True()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenCarousel>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.PagerOverlay, true);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-carousel-pager-overlay", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Carousel_Renders_PagerOverlay_False()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenCarousel>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.PagerOverlay, false);
|
||||
});
|
||||
|
||||
Assert.DoesNotContain("rz-carousel-pager-overlay", component.Markup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
369
Radzen.Blazor.Tests/ChatTests.cs
Normal file
369
Radzen.Blazor.Tests/ChatTests.cs
Normal file
@@ -0,0 +1,369 @@
|
||||
using Bunit;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Radzen.Blazor;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class ChatTests
|
||||
{
|
||||
[Fact]
|
||||
public void RadzenChat_ShouldRenderWithTitle()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenChat>(parameters => parameters
|
||||
.Add(p => p.Title, "Test Chat")
|
||||
.Add(p => p.CurrentUserId, "user1")
|
||||
.Add(p => p.Users, new List<ChatUser>())
|
||||
.Add(p => p.Messages, new List<ChatMessage>())
|
||||
);
|
||||
|
||||
Assert.Contains("Test Chat", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RadzenChat_ShouldShowEmptyMessageWhenNoMessages()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenChat>(parameters => parameters
|
||||
.Add(p => p.Title, "Test Chat")
|
||||
.Add(p => p.CurrentUserId, "user1")
|
||||
.Add(p => p.Users, new List<ChatUser>())
|
||||
.Add(p => p.Messages, new List<ChatMessage>())
|
||||
.Add(p => p.EmptyMessage, "No messages yet!")
|
||||
);
|
||||
|
||||
Assert.Contains("No messages yet!", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RadzenChat_ShouldDisplayMessages()
|
||||
{
|
||||
var messages = new List<ChatMessage>
|
||||
{
|
||||
new ChatMessage { Content = "Hello", UserId = "user1", Timestamp = DateTime.Now },
|
||||
new ChatMessage { Content = "Hi there!", UserId = "user2", Timestamp = DateTime.Now }
|
||||
};
|
||||
|
||||
var users = new List<ChatUser>
|
||||
{
|
||||
new ChatUser { Id = "user1", Name = "John" },
|
||||
new ChatUser { Id = "user2", Name = "Jane" }
|
||||
};
|
||||
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenChat>(parameters => parameters
|
||||
.Add(p => p.Title, "Test Chat")
|
||||
.Add(p => p.CurrentUserId, "user1")
|
||||
.Add(p => p.Users, users)
|
||||
.Add(p => p.Messages, messages)
|
||||
);
|
||||
|
||||
Assert.Contains("Hello", component.Markup);
|
||||
Assert.Contains("Hi there!", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RadzenChat_ShouldShowUsersInHeader()
|
||||
{
|
||||
var users = new List<ChatUser>
|
||||
{
|
||||
new ChatUser { Id = "user1", Name = "John" },
|
||||
new ChatUser { Id = "user2", Name = "Jane" }
|
||||
};
|
||||
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenChat>(parameters => parameters
|
||||
.Add(p => p.Title, "Test Chat")
|
||||
.Add(p => p.CurrentUserId, "user1")
|
||||
.Add(p => p.Users, users)
|
||||
.Add(p => p.Messages, new List<ChatMessage>())
|
||||
.Add(p => p.ShowUsers, true)
|
||||
);
|
||||
|
||||
Assert.Contains("John", component.Markup);
|
||||
Assert.Contains("Jane", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RadzenChat_ShouldShowUserNamesAboveMessages()
|
||||
{
|
||||
var messages = new List<ChatMessage>
|
||||
{
|
||||
new ChatMessage { Content = "Hello", UserId = "user2", Timestamp = DateTime.Now }
|
||||
};
|
||||
|
||||
var users = new List<ChatUser>
|
||||
{
|
||||
new ChatUser { Id = "user1", Name = "John" },
|
||||
new ChatUser { Id = "user2", Name = "Jane" }
|
||||
};
|
||||
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenChat>(parameters => parameters
|
||||
.Add(p => p.Title, "Test Chat")
|
||||
.Add(p => p.CurrentUserId, "user1")
|
||||
.Add(p => p.Users, users)
|
||||
.Add(p => p.Messages, messages)
|
||||
.Add(p => p.ShowUserNames, true)
|
||||
);
|
||||
|
||||
Assert.Contains("Jane", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RadzenChat_ShouldShowClearButtonWhenEnabled()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenChat>(parameters => parameters
|
||||
.Add(p => p.Title, "Test Chat")
|
||||
.Add(p => p.CurrentUserId, "user1")
|
||||
.Add(p => p.Users, new List<ChatUser>())
|
||||
.Add(p => p.Messages, new List<ChatMessage>())
|
||||
.Add(p => p.ShowClearButton, true)
|
||||
);
|
||||
|
||||
var clearButton = component.Find(".rz-chat-header-clear");
|
||||
Assert.NotNull(clearButton);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RadzenChat_ShouldNotShowClearButtonWhenDisabled()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenChat>(parameters => parameters
|
||||
.Add(p => p.Title, "Test Chat")
|
||||
.Add(p => p.CurrentUserId, "user1")
|
||||
.Add(p => p.Users, new List<ChatUser>())
|
||||
.Add(p => p.Messages, new List<ChatMessage>())
|
||||
.Add(p => p.ShowClearButton, false)
|
||||
);
|
||||
|
||||
var clearButton = component.FindAll(".rz-chat-header-clear");
|
||||
Assert.Empty(clearButton);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RadzenChat_ShouldLimitVisibleUsers()
|
||||
{
|
||||
var users = new List<ChatUser>
|
||||
{
|
||||
new ChatUser { Id = "user1", Name = "John" },
|
||||
new ChatUser { Id = "user2", Name = "Jane" },
|
||||
new ChatUser { Id = "user3", Name = "Bob" },
|
||||
new ChatUser { Id = "user4", Name = "Alice" },
|
||||
new ChatUser { Id = "user5", Name = "Charlie" },
|
||||
new ChatUser { Id = "user6", Name = "David" }
|
||||
};
|
||||
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenChat>(parameters => parameters
|
||||
.Add(p => p.Title, "Test Chat")
|
||||
.Add(p => p.CurrentUserId, "user1")
|
||||
.Add(p => p.Users, users)
|
||||
.Add(p => p.Messages, new List<ChatMessage>())
|
||||
.Add(p => p.ShowUsers, true)
|
||||
.Add(p => p.MaxVisibleUsers, 3)
|
||||
);
|
||||
|
||||
// Should show "+3" for the remaining users
|
||||
Assert.Contains("+3", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RadzenChat_ShouldShowUserMessagesOnRight()
|
||||
{
|
||||
var messages = new List<ChatMessage>
|
||||
{
|
||||
new ChatMessage { Content = "My message", UserId = "user1", Timestamp = DateTime.Now }
|
||||
};
|
||||
|
||||
var users = new List<ChatUser>
|
||||
{
|
||||
new ChatUser { Id = "user1", Name = "John" }
|
||||
};
|
||||
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenChat>(parameters => parameters
|
||||
.Add(p => p.Title, "Test Chat")
|
||||
.Add(p => p.CurrentUserId, "user1")
|
||||
.Add(p => p.Users, users)
|
||||
.Add(p => p.Messages, messages)
|
||||
);
|
||||
|
||||
var userMessage = component.Find(".rz-chat-message-user");
|
||||
Assert.NotNull(userMessage);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RadzenChat_ShouldShowUserMessagesOnLeft()
|
||||
{
|
||||
var messages = new List<ChatMessage>
|
||||
{
|
||||
new ChatMessage { Content = "Other message", UserId = "user2", Timestamp = DateTime.Now }
|
||||
};
|
||||
|
||||
var users = new List<ChatUser>
|
||||
{
|
||||
new ChatUser { Id = "user1", Name = "John" },
|
||||
new ChatUser { Id = "user2", Name = "Jane" }
|
||||
};
|
||||
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenChat>(parameters => parameters
|
||||
.Add(p => p.Title, "Test Chat")
|
||||
.Add(p => p.CurrentUserId, "user1")
|
||||
.Add(p => p.Users, users)
|
||||
.Add(p => p.Messages, messages)
|
||||
);
|
||||
|
||||
var participantMessage = component.Find(".rz-chat-message-participant");
|
||||
Assert.NotNull(participantMessage);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RadzenChat_ShouldShowAvatarInitials()
|
||||
{
|
||||
var users = new List<ChatUser>
|
||||
{
|
||||
new ChatUser { Id = "user1", Name = "John Doe" }
|
||||
};
|
||||
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenChat>(parameters => parameters
|
||||
.Add(p => p.Title, "Test Chat")
|
||||
.Add(p => p.CurrentUserId, "user1")
|
||||
.Add(p => p.Users, users)
|
||||
.Add(p => p.Messages, new List<ChatMessage>())
|
||||
.Add(p => p.ShowUsers, true)
|
||||
);
|
||||
|
||||
Assert.Contains("JD", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RadzenChat_ShouldShowAvatarImageWhenProvided()
|
||||
{
|
||||
var users = new List<ChatUser>
|
||||
{
|
||||
new ChatUser { Id = "user1", Name = "John", AvatarUrl = "https://example.com/avatar.jpg" }
|
||||
};
|
||||
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenChat>(parameters => parameters
|
||||
.Add(p => p.Title, "Test Chat")
|
||||
.Add(p => p.CurrentUserId, "user1")
|
||||
.Add(p => p.Users, users)
|
||||
.Add(p => p.Messages, new List<ChatMessage>())
|
||||
.Add(p => p.ShowUsers, true)
|
||||
);
|
||||
|
||||
var avatarImage = component.Find(".rz-chat-participant-image");
|
||||
Assert.NotNull(avatarImage);
|
||||
Assert.Equal("https://example.com/avatar.jpg", avatarImage.GetAttribute("src"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RadzenChat_ShouldBeDisabledWhenDisabled()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenChat>(parameters => parameters
|
||||
.Add(p => p.Title, "Test Chat")
|
||||
.Add(p => p.CurrentUserId, "user1")
|
||||
.Add(p => p.Users, new List<ChatUser>())
|
||||
.Add(p => p.Messages, new List<ChatMessage>())
|
||||
.Add(p => p.Disabled, true)
|
||||
);
|
||||
|
||||
var textarea = component.Find(".rz-chat-textarea");
|
||||
Assert.True(textarea.HasAttribute("disabled"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RadzenChat_ShouldBeReadOnlyWhenReadOnly()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenChat>(parameters => parameters
|
||||
.Add(p => p.Title, "Test Chat")
|
||||
.Add(p => p.CurrentUserId, "user1")
|
||||
.Add(p => p.Users, new List<ChatUser>())
|
||||
.Add(p => p.Messages, new List<ChatMessage>())
|
||||
.Add(p => p.ReadOnly, true)
|
||||
);
|
||||
|
||||
var textarea = component.Find(".rz-chat-textarea");
|
||||
Assert.True(textarea.HasAttribute("readonly"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RadzenChat_ShouldShowCustomPlaceholder()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenChat>(parameters => parameters
|
||||
.Add(p => p.Title, "Test Chat")
|
||||
.Add(p => p.CurrentUserId, "user1")
|
||||
.Add(p => p.Users, new List<ChatUser>())
|
||||
.Add(p => p.Messages, new List<ChatMessage>())
|
||||
.Add(p => p.Placeholder, "Custom placeholder")
|
||||
);
|
||||
|
||||
var textarea = component.Find(".rz-chat-textarea");
|
||||
Assert.Equal("Custom placeholder", textarea.GetAttribute("placeholder"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RadzenChat_ShouldShowStreamingIndicator()
|
||||
{
|
||||
var messages = new List<ChatMessage>
|
||||
{
|
||||
new ChatMessage { Content = "Streaming message", UserId = "user1", IsStreaming = true, Timestamp = DateTime.Now }
|
||||
};
|
||||
|
||||
var users = new List<ChatUser>
|
||||
{
|
||||
new ChatUser { Id = "user1", Name = "John" }
|
||||
};
|
||||
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenChat>(parameters => parameters
|
||||
.Add(p => p.Title, "Test Chat")
|
||||
.Add(p => p.CurrentUserId, "user1")
|
||||
.Add(p => p.Users, users)
|
||||
.Add(p => p.Messages, messages)
|
||||
);
|
||||
|
||||
var streamingIcon = component.Find(".rz-chat-message-streaming-icon");
|
||||
Assert.NotNull(streamingIcon);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RadzenChat_ShouldShowMessageTimestamps()
|
||||
{
|
||||
var timestamp = DateTime.Now.AddHours(-1);
|
||||
var messages = new List<ChatMessage>
|
||||
{
|
||||
new ChatMessage { Content = "Test message", UserId = "user1", Timestamp = timestamp }
|
||||
};
|
||||
|
||||
var users = new List<ChatUser>
|
||||
{
|
||||
new ChatUser { Id = "user1", Name = "John" }
|
||||
};
|
||||
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenChat>(parameters => parameters
|
||||
.Add(p => p.Title, "Test Chat")
|
||||
.Add(p => p.CurrentUserId, "user1")
|
||||
.Add(p => p.Users, users)
|
||||
.Add(p => p.Messages, messages)
|
||||
);
|
||||
|
||||
Assert.Contains(timestamp.ToString("HH:mm"), component.Markup);
|
||||
}
|
||||
}
|
||||
}
|
||||
168
Radzen.Blazor.Tests/CheckBoxListTests.cs
Normal file
168
Radzen.Blazor.Tests/CheckBoxListTests.cs
Normal file
@@ -0,0 +1,168 @@
|
||||
using Bunit;
|
||||
using Xunit;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class CheckBoxListTests
|
||||
{
|
||||
class Item
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public bool Disabled { get; set; }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CheckBoxList_Renders_WithClassName()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenCheckBoxList<int>>();
|
||||
|
||||
Assert.Contains(@"rz-checkbox-list", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CheckBoxList_Renders_WithData()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var data = new List<string> { "Option 1", "Option 2", "Option 3" };
|
||||
|
||||
var component = ctx.RenderComponent<RadzenCheckBoxList<string>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Data, data);
|
||||
});
|
||||
|
||||
Assert.Contains("Option 1", component.Markup);
|
||||
Assert.Contains("Option 2", component.Markup);
|
||||
Assert.Contains("Option 3", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CheckBoxList_Renders_WithCustomTextValueProperties()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var data = new List<Item>
|
||||
{
|
||||
new Item { Id = 1, Name = "First" },
|
||||
new Item { Id = 2, Name = "Second" }
|
||||
};
|
||||
|
||||
var component = ctx.RenderComponent<RadzenCheckBoxList<int>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Data, data);
|
||||
parameters.Add(p => p.TextProperty, "Name");
|
||||
parameters.Add(p => p.ValueProperty, "Id");
|
||||
});
|
||||
|
||||
Assert.Contains("First", component.Markup);
|
||||
Assert.Contains("Second", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CheckBoxList_Renders_Orientation_Horizontal()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenCheckBoxList<int>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Orientation, Orientation.Horizontal);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-flex-row", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CheckBoxList_Renders_Orientation_Vertical()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenCheckBoxList<int>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Orientation, Orientation.Vertical);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-flex-column", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CheckBoxList_Renders_Disabled()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenCheckBoxList<int>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Disabled, true);
|
||||
});
|
||||
|
||||
Assert.Contains("disabled", component.Markup);
|
||||
Assert.Contains("rz-state-disabled", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CheckBoxList_Renders_AllowSelectAll()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var data = new List<string> { "Option 1", "Option 2" };
|
||||
|
||||
var component = ctx.RenderComponent<RadzenCheckBoxList<string>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowSelectAll, true);
|
||||
parameters.Add(p => p.Data, data);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-multiselect-header", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CheckBoxList_Renders_SelectAllText()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var data = new List<string> { "Option 1", "Option 2" };
|
||||
|
||||
var component = ctx.RenderComponent<RadzenCheckBoxList<string>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowSelectAll, true);
|
||||
parameters.Add(p => p.SelectAllText, "Select All Options");
|
||||
parameters.Add(p => p.Data, data);
|
||||
});
|
||||
|
||||
Assert.Contains("Select All Options", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CheckBoxList_Renders_CheckboxInputs()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var data = new List<string> { "Option 1", "Option 2" };
|
||||
|
||||
var component = ctx.RenderComponent<RadzenCheckBoxList<string>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Data, data);
|
||||
});
|
||||
|
||||
Assert.Contains("type=\"checkbox\"", component.Markup);
|
||||
Assert.Contains("rz-chkbox", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CheckBoxList_Renders_DisabledItems()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var data = new List<Item>
|
||||
{
|
||||
new Item { Id = 1, Name = "Enabled", Disabled = false },
|
||||
new Item { Id = 2, Name = "Disabled", Disabled = true }
|
||||
};
|
||||
|
||||
var component = ctx.RenderComponent<RadzenCheckBoxList<int>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Data, data);
|
||||
parameters.Add(p => p.TextProperty, "Name");
|
||||
parameters.Add(p => p.ValueProperty, "Id");
|
||||
parameters.Add(p => p.DisabledProperty, "Disabled");
|
||||
});
|
||||
|
||||
Assert.Contains("Enabled", component.Markup);
|
||||
Assert.Contains("Disabled", component.Markup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
74
Radzen.Blazor.Tests/ColumnTests.cs
Normal file
74
Radzen.Blazor.Tests/ColumnTests.cs
Normal file
@@ -0,0 +1,74 @@
|
||||
using Bunit;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class ColumnTests
|
||||
{
|
||||
[Fact]
|
||||
public void Column_Renders_WithClassName()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenColumn>();
|
||||
|
||||
Assert.Contains(@"rz-col", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Column_Renders_ChildContent()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenColumn>(parameters =>
|
||||
{
|
||||
parameters.AddChildContent("<div>Column Content</div>");
|
||||
});
|
||||
|
||||
Assert.Contains("Column Content", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Column_Renders_SizeParameter()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenColumn>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Size, 6));
|
||||
|
||||
Assert.Contains("rz-col-6", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Column_Renders_SizeMD()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenColumn>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.SizeMD, 4));
|
||||
|
||||
Assert.Contains("rz-col-md-4", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Column_Renders_SizeSM()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenColumn>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.SizeSM, 12));
|
||||
|
||||
Assert.Contains("rz-col-sm-12", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Column_Renders_Offset()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenColumn>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Offset, 2));
|
||||
|
||||
Assert.Contains("rz-offset-2", component.Markup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -732,5 +732,130 @@ namespace Radzen.Blazor.Tests
|
||||
var weekNumberHeader = component.Find(".rz-calendar-view th.rz-datepicker-week-number");
|
||||
Assert.Contains("Wk", weekNumberHeader.InnerHtml);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DatePicker_Multiple_Selects_IEnumerableDateTime()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
IEnumerable<DateTime> emitted = null;
|
||||
var initial = new DateTime(2024, 1, 1);
|
||||
|
||||
var component = ctx.RenderComponent<RadzenDatePicker<IEnumerable<DateTime>>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Multiple, true);
|
||||
parameters.Add(p => p.InitialViewDate, initial);
|
||||
parameters.Add(p => p.ValueChanged, args => { emitted = args; });
|
||||
});
|
||||
|
||||
component.InvokeAsync(() => component.FindAll("td:not(.rz-calendar-other-month) span").First(e => e.TextContent == "10").ParentElement.Click());
|
||||
component.InvokeAsync(() => component.FindAll("td:not(.rz-calendar-other-month) span").First(e => e.TextContent == "12").ParentElement.Click());
|
||||
|
||||
Assert.NotNull(emitted);
|
||||
var list = emitted.ToList();
|
||||
Assert.Equal(2, list.Count);
|
||||
Assert.Contains(new DateTime(2024, 1, 10), list.Select(d => d.Date));
|
||||
Assert.Contains(new DateTime(2024, 1, 12), list.Select(d => d.Date));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DatePicker_Multiple_Selects_IEnumerableNullableDateTime()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
IEnumerable<DateTime?> emitted = null;
|
||||
var initial = new DateTime(2024, 2, 1);
|
||||
|
||||
var component = ctx.RenderComponent<RadzenDatePicker<IEnumerable<DateTime?>>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Multiple, true);
|
||||
parameters.Add(p => p.InitialViewDate, initial);
|
||||
parameters.Add(p => p.ValueChanged, args => { emitted = args; });
|
||||
});
|
||||
|
||||
component.InvokeAsync(() => component.FindAll("td:not(.rz-calendar-other-month) span").First(e => e.TextContent == "3").ParentElement.Click());
|
||||
component.InvokeAsync(() => component.FindAll("td:not(.rz-calendar-other-month) span").First(e => e.TextContent == "14").ParentElement.Click());
|
||||
|
||||
Assert.NotNull(emitted);
|
||||
var list = emitted.ToList();
|
||||
Assert.Equal(2, list.Count);
|
||||
Assert.Contains(new DateTime(2024, 2, 3), list.Select(d => d.Value.Date));
|
||||
Assert.Contains(new DateTime(2024, 2, 14), list.Select(d => d.Value.Date));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DatePicker_Multiple_Emits_IEnumerableDateTimeOffsetNullable_WithUtcKind()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
IEnumerable<DateTimeOffset?> emitted = null;
|
||||
var initial = new DateTime(2024, 3, 1);
|
||||
|
||||
var component = ctx.RenderComponent<RadzenDatePicker<IEnumerable<DateTimeOffset?>>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Multiple, true);
|
||||
parameters.Add(p => p.InitialViewDate, initial);
|
||||
parameters.Add(p => p.Kind, DateTimeKind.Utc);
|
||||
parameters.Add(p => p.ValueChanged, args => { emitted = args; });
|
||||
});
|
||||
|
||||
component.InvokeAsync(() => component.FindAll("td:not(.rz-calendar-other-month) span").First(e => e.TextContent == "5").ParentElement.Click());
|
||||
component.InvokeAsync(() => component.FindAll("td:not(.rz-calendar-other-month) span").First(e => e.TextContent == "20").ParentElement.Click());
|
||||
|
||||
Assert.NotNull(emitted);
|
||||
var list = emitted.ToList();
|
||||
Assert.Equal(2, list.Count);
|
||||
Assert.All(list, dto => Assert.Equal(TimeSpan.Zero, dto.Value.Offset));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DatePicker_Multiple_Emits_IEnumerableDateOnlyAndTimeOnlyNullable()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
IEnumerable<DateOnly?> emittedDates = null;
|
||||
IEnumerable<TimeOnly?> emittedTimes = null;
|
||||
var initial = new DateTime(2024, 4, 1);
|
||||
|
||||
var compDates = ctx.RenderComponent<RadzenDatePicker<IEnumerable<DateOnly?>>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Multiple, true);
|
||||
parameters.Add(p => p.InitialViewDate, initial);
|
||||
parameters.Add(p => p.ValueChanged, args => { emittedDates = args; });
|
||||
});
|
||||
|
||||
compDates.InvokeAsync(() => compDates.FindAll("td:not(.rz-calendar-other-month) span").First(e => e.TextContent == "7").ParentElement.Click());
|
||||
compDates.InvokeAsync(() => compDates.FindAll("td:not(.rz-calendar-other-month) span").First(e => e.TextContent == "9").ParentElement.Click());
|
||||
|
||||
Assert.NotNull(emittedDates);
|
||||
var dateList = emittedDates.ToList();
|
||||
Assert.Equal(2, dateList.Count);
|
||||
Assert.Contains(new DateOnly(2024, 4, 7), dateList.Select(d => d.Value));
|
||||
Assert.Contains(new DateOnly(2024, 4, 9), dateList.Select(d => d.Value));
|
||||
|
||||
// TimeOnly? emission should produce midnight times for selected dates
|
||||
var compTimes = ctx.RenderComponent<RadzenDatePicker<IEnumerable<TimeOnly?>>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Multiple, true);
|
||||
parameters.Add(p => p.InitialViewDate, initial);
|
||||
parameters.Add(p => p.ValueChanged, args => { emittedTimes = args; });
|
||||
});
|
||||
|
||||
compTimes.InvokeAsync(() => compTimes.FindAll("td:not(.rz-calendar-other-month) span").First(e => e.TextContent == "1").ParentElement.Click());
|
||||
compTimes.InvokeAsync(() => compTimes.FindAll("td:not(.rz-calendar-other-month) span").First(e => e.TextContent == "2").ParentElement.Click());
|
||||
|
||||
Assert.NotNull(emittedTimes);
|
||||
var timeList = emittedTimes.ToList();
|
||||
Assert.Equal(2, timeList.Count);
|
||||
Assert.All(timeList, t => Assert.Equal(new TimeOnly(0, 0, 0), t.Value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
using System;
|
||||
using Bunit;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class DialogServiceTests
|
||||
public class DialogServiceTests : ComponentBase
|
||||
{
|
||||
public class OpenDialogTests
|
||||
{
|
||||
@@ -124,13 +128,81 @@ namespace Radzen.Blazor.Tests
|
||||
var openTask = dialogService.OpenAsync("Dynamic Open", typeof(RadzenButton), []);
|
||||
dialogService.Close();
|
||||
await openTask;
|
||||
|
||||
|
||||
// Assert
|
||||
Assert.Equal("Dynamic Open", resultingTitle);
|
||||
Assert.Equal(typeof(RadzenButton), resultingType);
|
||||
}
|
||||
}
|
||||
|
||||
public class OpenSideDialogTests
|
||||
{
|
||||
[Fact(DisplayName = "SideDialogOptions resizable option is retained after OpenSideDialog call")]
|
||||
public void SideDialogOptions_Resizable_AreRetained_AfterOpenSideDialogCall()
|
||||
{
|
||||
// Arrange
|
||||
var options = new SideDialogOptions { Resizable = true };
|
||||
SideDialogOptions resultingOptions = null;
|
||||
var dialogService = new DialogService(null, null);
|
||||
dialogService.OnSideOpen += (_, _, sideOptions) => resultingOptions = sideOptions;
|
||||
|
||||
// Act
|
||||
dialogService.OpenSide<DialogServiceTests>("Test", [], options);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(resultingOptions);
|
||||
Assert.Same(options, resultingOptions);
|
||||
Assert.True(resultingOptions.Resizable);
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Side dialog shows resize bar when Resizable is true")]
|
||||
public void SideDialog_Resizable_ShowsResizeBar()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.Services.AddScoped<DialogService>();
|
||||
|
||||
// Render the dialog host
|
||||
var cut = ctx.RenderComponent<RadzenDialog>();
|
||||
|
||||
// Open a side dialog with Resizable=true
|
||||
var dialogService = ctx.Services.GetRequiredService<DialogService>();
|
||||
cut.InvokeAsync(() => dialogService.OpenSide("Test", typeof(RadzenButton),
|
||||
new Dictionary<string, object>(), new SideDialogOptions { Resizable = true }));
|
||||
|
||||
// Assert: the resize bar element is present
|
||||
cut.WaitForAssertion(() =>
|
||||
{
|
||||
var markup = cut.Markup;
|
||||
Assert.Contains("rz-dialog-resize-bar", markup);
|
||||
// Optionally ensure the inner handle exists too
|
||||
Assert.Contains("rz-resize", markup);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Side dialog hides resize bar when Resizable is false")]
|
||||
public void SideDialog_NonResizable_HidesResizeBar()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.Services.AddScoped<DialogService>();
|
||||
|
||||
// Render the dialog host
|
||||
var cut = ctx.RenderComponent<RadzenDialog>();
|
||||
|
||||
// Open a side dialog with Resizable=false
|
||||
var dialogService = ctx.Services.GetRequiredService<DialogService>();
|
||||
cut.InvokeAsync(() => dialogService.OpenSide("Test", typeof(RadzenButton),
|
||||
new Dictionary<string, object>(), new SideDialogOptions()));
|
||||
|
||||
// Assert: the resize bar element is not present
|
||||
cut.WaitForAssertion(() =>
|
||||
{
|
||||
var markup = cut.Markup;
|
||||
Assert.DoesNotContain("rz-dialog-resize-bar", markup);
|
||||
});
|
||||
}
|
||||
}
|
||||
public class ConfirmTests
|
||||
{
|
||||
[Fact(DisplayName = "ConfirmOptions is null and default values are set correctly")]
|
||||
|
||||
289
Radzen.Blazor.Tests/DropDownDataGridTests.cs
Normal file
289
Radzen.Blazor.Tests/DropDownDataGridTests.cs
Normal file
@@ -0,0 +1,289 @@
|
||||
using Bunit;
|
||||
using Xunit;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class DropDownDataGridTests
|
||||
{
|
||||
class Customer
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string CompanyName { get; set; }
|
||||
public string ContactName { get; set; }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DropDownDataGrid_Renders_WithClassName()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenDropDownDataGrid<int>>();
|
||||
|
||||
Assert.Contains(@"rz-dropdown", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DropDownDataGrid_Renders_DropdownTrigger()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenDropDownDataGrid<int>>();
|
||||
|
||||
Assert.Contains("rz-dropdown-trigger", component.Markup);
|
||||
Assert.Contains("rzi-chevron-down", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DropDownDataGrid_Renders_WithData()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var data = new List<string> { "Item1", "Item2", "Item3" };
|
||||
|
||||
var component = ctx.RenderComponent<RadzenDropDownDataGrid<string>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Data, data);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-lookup-panel", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DropDownDataGrid_Renders_WithCustomTextValueProperties()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var data = new List<Customer>
|
||||
{
|
||||
new Customer { Id = 1, CompanyName = "Acme Corp", ContactName = "John Doe" },
|
||||
new Customer { Id = 2, CompanyName = "Tech Inc", ContactName = "Jane Smith" }
|
||||
};
|
||||
|
||||
var component = ctx.RenderComponent<RadzenDropDownDataGrid<int>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Data, data);
|
||||
parameters.Add(p => p.TextProperty, "CompanyName");
|
||||
parameters.Add(p => p.ValueProperty, "Id");
|
||||
});
|
||||
|
||||
Assert.Contains("rz-lookup-panel", component.Markup);
|
||||
Assert.Contains("rz-data-grid", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DropDownDataGrid_Renders_DataGrid()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var data = new List<string> { "Item1" };
|
||||
|
||||
var component = ctx.RenderComponent<RadzenDropDownDataGrid<string>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Data, data);
|
||||
});
|
||||
|
||||
// DropDownDataGrid embeds a DataGrid
|
||||
Assert.Contains("rz-data-grid", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DropDownDataGrid_Renders_AllowFiltering()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var data = new List<string> { "Item1", "Item2" };
|
||||
|
||||
var component = ctx.RenderComponent<RadzenDropDownDataGrid<string>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowFiltering, true);
|
||||
parameters.Add(p => p.Data, data);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-lookup-search", component.Markup);
|
||||
Assert.Contains("rz-lookup-search-input", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DropDownDataGrid_Renders_Placeholder()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenDropDownDataGrid<int>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Placeholder, "Select an item");
|
||||
});
|
||||
|
||||
Assert.Contains("Select an item", component.Markup);
|
||||
Assert.Contains("rz-placeholder", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DropDownDataGrid_Renders_AllowClear_WithValue()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var data = new List<string> { "Item1" };
|
||||
|
||||
var component = ctx.RenderComponent<RadzenDropDownDataGrid<string>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowClear, true);
|
||||
parameters.Add(p => p.Data, data);
|
||||
parameters.Add(p => p.Value, "Item1");
|
||||
});
|
||||
|
||||
Assert.Contains("rz-dropdown-clear-icon", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DropDownDataGrid_DoesNotRender_AllowClear_WhenNotAllowed()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var data = new List<string> { "Item1" };
|
||||
|
||||
var component = ctx.RenderComponent<RadzenDropDownDataGrid<string>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowClear, false);
|
||||
parameters.Add(p => p.Data, data);
|
||||
parameters.Add(p => p.Value, "Item1");
|
||||
});
|
||||
|
||||
Assert.DoesNotContain("rz-dropdown-clear-icon", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DropDownDataGrid_Renders_Disabled()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenDropDownDataGrid<int>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Disabled, true);
|
||||
});
|
||||
|
||||
Assert.Contains("disabled", component.Markup);
|
||||
Assert.Contains("rz-state-disabled", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DropDownDataGrid_Renders_Multiple_Panel()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenDropDownDataGrid<IEnumerable<int>>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Multiple, true);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-multiselect-panel", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DropDownDataGrid_Renders_Multiple_WithChips()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var data = new List<string> { "Item1", "Item2" };
|
||||
var selectedItems = new List<string> { "Item1" };
|
||||
|
||||
var component = ctx.RenderComponent<RadzenDropDownDataGrid<IEnumerable<string>>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Multiple, true);
|
||||
parameters.Add(p => p.Chips, true);
|
||||
parameters.Add(p => p.Data, data);
|
||||
parameters.Add(p => p.Value, selectedItems);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-dropdown-chips-wrapper", component.Markup);
|
||||
Assert.Contains("rz-chip", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DropDownDataGrid_Renders_AllowSorting()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var data = new List<Customer>
|
||||
{
|
||||
new Customer { Id = 1, CompanyName = "Acme" }
|
||||
};
|
||||
|
||||
var component = ctx.RenderComponent<RadzenDropDownDataGrid<int>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowSorting, true);
|
||||
parameters.Add(p => p.Data, data);
|
||||
parameters.Add(p => p.TextProperty, "CompanyName");
|
||||
});
|
||||
|
||||
Assert.Contains("rz-data-grid", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DropDownDataGrid_Renders_SearchTextPlaceholder()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var data = new List<string> { "Item1" };
|
||||
|
||||
var component = ctx.RenderComponent<RadzenDropDownDataGrid<string>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowFiltering, true);
|
||||
parameters.Add(p => p.SearchTextPlaceholder, "Type to filter...");
|
||||
parameters.Add(p => p.Data, data);
|
||||
});
|
||||
|
||||
Assert.Contains("placeholder=\"Type to filter...\"", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DropDownDataGrid_Renders_EmptyText()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
|
||||
var component = ctx.RenderComponent<RadzenDropDownDataGrid<string>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Data, new List<string>());
|
||||
parameters.Add(p => p.EmptyText, "No items found");
|
||||
});
|
||||
|
||||
Assert.Contains("No items found", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DropDownDataGrid_Renders_PageSize()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var data = Enumerable.Range(1, 20).Select(i => $"Item {i}").ToList();
|
||||
|
||||
var component = ctx.RenderComponent<RadzenDropDownDataGrid<string>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Data, data);
|
||||
parameters.Add(p => p.PageSize, 10);
|
||||
});
|
||||
|
||||
// DataGrid with paging should be present
|
||||
Assert.Contains("rz-data-grid", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DropDownDataGrid_Renders_AllowRowSelectOnRowClick()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var data = new List<string> { "Item1", "Item2" };
|
||||
|
||||
var component = ctx.RenderComponent<RadzenDropDownDataGrid<string>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowRowSelectOnRowClick, true);
|
||||
parameters.Add(p => p.Data, data);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-data-grid", component.Markup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -686,5 +686,58 @@ namespace Radzen.Blazor.Tests
|
||||
public IEnumerator<T> GetEnumerator() => _items.GetEnumerator();
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DropDown_Renders_Placeholder()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
|
||||
var component = ctx.RenderComponent<RadzenDropDown<int>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Placeholder, "Select an option");
|
||||
});
|
||||
|
||||
Assert.Contains("Select an option", component.Markup);
|
||||
Assert.Contains("rz-placeholder", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DropDown_Renders_AllowClear_WithValue()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var data = new[] { new DataItem { Text = "Item 1", Id = 1 } };
|
||||
|
||||
var component = ctx.RenderComponent<RadzenDropDown<int>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowClear, true);
|
||||
parameters.Add(p => p.Data, data);
|
||||
parameters.Add(p => p.TextProperty, nameof(DataItem.Text));
|
||||
parameters.Add(p => p.ValueProperty, nameof(DataItem.Id));
|
||||
parameters.Add(p => p.Value, 1);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-dropdown-clear-icon", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DropDown_DoesNotRender_AllowClear_WhenNotAllowed()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var data = new[] { new DataItem { Text = "Item 1", Id = 1 } };
|
||||
|
||||
var component = ctx.RenderComponent<RadzenDropDown<int>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowClear, false);
|
||||
parameters.Add(p => p.Data, data);
|
||||
parameters.Add(p => p.TextProperty, nameof(DataItem.Text));
|
||||
parameters.Add(p => p.ValueProperty, nameof(DataItem.Id));
|
||||
parameters.Add(p => p.Value, 1);
|
||||
});
|
||||
|
||||
Assert.DoesNotContain("rz-dropdown-clear-icon", component.Markup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace Radzen.Blazor.Tests
|
||||
public Guid Id { get; set; }
|
||||
public TimeOnly StartTime { get; set; }
|
||||
public DateOnly BirthDate { get; set; }
|
||||
public int[] Scores { get; set; }
|
||||
public IEnumerable<int> Scores { get; set; }
|
||||
public List<string> Tags { get; set; }
|
||||
public List<TestEntity> Children { get; set; }
|
||||
public Address Address { get; set; }
|
||||
|
||||
182
Radzen.Blazor.Tests/FileInputTests.cs
Normal file
182
Radzen.Blazor.Tests/FileInputTests.cs
Normal file
@@ -0,0 +1,182 @@
|
||||
using Bunit;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class FileInputTests
|
||||
{
|
||||
[Fact]
|
||||
public void FileInput_Renders_WithClassName()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenFileInput<string>>();
|
||||
|
||||
Assert.Contains(@"rz-fileupload", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FileInput_Renders_ChooseButton()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenFileInput<string>>();
|
||||
|
||||
Assert.Contains("rz-fileupload-choose", component.Markup);
|
||||
Assert.Contains("rz-button", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FileInput_Renders_ChooseText()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenFileInput<string>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.ChooseText, "Select File");
|
||||
});
|
||||
|
||||
Assert.Contains("Select File", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FileInput_Renders_DefaultChooseText()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenFileInput<string>>();
|
||||
|
||||
Assert.Contains("Choose", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FileInput_Renders_Disabled()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenFileInput<string>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Disabled, true);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-state-disabled", component.Markup);
|
||||
Assert.Contains("disabled", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FileInput_Renders_Accept()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenFileInput<string>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Accept, "application/pdf");
|
||||
});
|
||||
|
||||
Assert.Contains("accept=\"application/pdf\"", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FileInput_Renders_DefaultAccept()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenFileInput<string>>();
|
||||
|
||||
Assert.Contains("accept=\"image/*\"", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FileInput_Renders_FileInputElement()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenFileInput<string>>();
|
||||
|
||||
Assert.Contains("type=\"file\"", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FileInput_Renders_Title_WhenSet()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenFileInput<string>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Title, "MyDocument.pdf");
|
||||
parameters.Add(p => p.Value, "data:application/pdf;base64,test");
|
||||
});
|
||||
|
||||
Assert.Contains("MyDocument.pdf", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FileInput_Renders_FileName_WhenTitleNotSet()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenFileInput<string>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.FileName, "document.pdf");
|
||||
parameters.Add(p => p.Value, "data:application/pdf;base64,test");
|
||||
});
|
||||
|
||||
Assert.Contains("document.pdf", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FileInput_Renders_DeleteButton_WhenValueSet()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenFileInput<string>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Value, "data:text/plain;base64,test");
|
||||
});
|
||||
|
||||
Assert.Contains("rz-icon-trash", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FileInput_Renders_CustomDeleteText()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenFileInput<string>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.DeleteText, "Remove File");
|
||||
parameters.Add(p => p.Value, "data:text/plain;base64,test");
|
||||
});
|
||||
|
||||
Assert.Contains("title=\"Remove File\"", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FileInput_Renders_ImagePreview_ForImageFile()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenFileInput<string>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Value, "data:image/png;base64,test");
|
||||
});
|
||||
|
||||
Assert.Contains("<img", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FileInput_Renders_ImageAlternateText()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenFileInput<string>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.ImageAlternateText, "User Photo");
|
||||
parameters.Add(p => p.Value, "data:image/png;base64,test");
|
||||
});
|
||||
|
||||
Assert.Contains("alt=\"User Photo\"", component.Markup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
163
Radzen.Blazor.Tests/FormFieldTests.cs
Normal file
163
Radzen.Blazor.Tests/FormFieldTests.cs
Normal file
@@ -0,0 +1,163 @@
|
||||
using Bunit;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class FormFieldTests
|
||||
{
|
||||
[Fact]
|
||||
public void FormField_Renders_WithClassName()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenFormField>();
|
||||
|
||||
Assert.Contains(@"rz-form-field", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FormField_Renders_Text()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenFormField>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Text, "Email Address");
|
||||
});
|
||||
|
||||
Assert.Contains("Email Address", component.Markup);
|
||||
Assert.Contains("rz-form-field-label", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FormField_Renders_Variant_Outlined()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenFormField>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Variant, Variant.Outlined);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-variant-outlined", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FormField_Renders_Variant_Filled()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenFormField>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Variant, Variant.Filled);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-variant-filled", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FormField_Renders_Variant_Flat()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenFormField>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Variant, Variant.Flat);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-variant-flat", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FormField_Renders_Variant_Text()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenFormField>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Variant, Variant.Text);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-variant-text", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FormField_Renders_AllowFloatingLabel_True()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenFormField>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowFloatingLabel, true);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-floating-label", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FormField_Renders_AllowFloatingLabel_False()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenFormField>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowFloatingLabel, false);
|
||||
});
|
||||
|
||||
Assert.DoesNotContain("rz-floating-label", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FormField_Renders_Component_Attribute()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenFormField>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Component, "email-input");
|
||||
});
|
||||
|
||||
Assert.Contains("for=\"email-input\"", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FormField_Renders_Helper()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenFormField>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Helper, builder => builder.AddContent(0, "Enter your email address"));
|
||||
});
|
||||
|
||||
Assert.Contains("rz-form-field-helper", component.Markup);
|
||||
Assert.Contains("Enter your email address", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FormField_Renders_Start()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenFormField>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Start, builder => builder.AddMarkupContent(0, "<span>Start</span>"));
|
||||
});
|
||||
|
||||
Assert.Contains("rz-form-field-start", component.Markup);
|
||||
Assert.Contains("Start", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FormField_Renders_End()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenFormField>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.End, builder => builder.AddMarkupContent(0, "<span>End</span>"));
|
||||
});
|
||||
|
||||
Assert.Contains("rz-form-field-end", component.Markup);
|
||||
Assert.Contains("End", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FormField_Renders_FormFieldContent()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenFormField>();
|
||||
|
||||
Assert.Contains("rz-form-field-content", component.Markup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
103
Radzen.Blazor.Tests/HeadingTests.cs
Normal file
103
Radzen.Blazor.Tests/HeadingTests.cs
Normal file
@@ -0,0 +1,103 @@
|
||||
using Bunit;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class HeadingTests
|
||||
{
|
||||
[Fact]
|
||||
public void Heading_Renders_WithClassName()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenHeading>();
|
||||
|
||||
Assert.Contains(@"rz-heading", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Heading_Renders_TextParameter()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenHeading>();
|
||||
|
||||
var text = "Test Heading";
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Text, text));
|
||||
|
||||
Assert.Contains(text, component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Heading_Renders_H1_ByDefault()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenHeading>();
|
||||
|
||||
var text = "Heading Text";
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Text, text));
|
||||
|
||||
Assert.Contains("<h1", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Heading_Renders_H2_Size()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenHeading>();
|
||||
|
||||
var text = "Heading 2";
|
||||
component.SetParametersAndRender(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Text, text);
|
||||
parameters.Add(p => p.Size, "H2");
|
||||
});
|
||||
|
||||
Assert.Contains("<h2", component.Markup);
|
||||
Assert.Contains(text, component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Heading_Renders_H3_Size()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenHeading>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Size, "H3"));
|
||||
|
||||
Assert.Contains("<h3", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Heading_Renders_H4_Size()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenHeading>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Size, "H4"));
|
||||
|
||||
Assert.Contains("<h4", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Heading_Renders_H5_Size()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenHeading>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Size, "H5"));
|
||||
|
||||
Assert.Contains("<h5", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Heading_Renders_H6_Size()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenHeading>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Size, "H6"));
|
||||
|
||||
Assert.Contains("<h6", component.Markup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
104
Radzen.Blazor.Tests/HtmlEditorTests.cs
Normal file
104
Radzen.Blazor.Tests/HtmlEditorTests.cs
Normal file
@@ -0,0 +1,104 @@
|
||||
using Bunit;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class HtmlEditorTests
|
||||
{
|
||||
[Fact]
|
||||
public void HtmlEditor_Renders_WithClassName()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.Services.AddScoped<DialogService>();
|
||||
var component = ctx.RenderComponent<RadzenHtmlEditor>();
|
||||
|
||||
Assert.Contains(@"rz-html-editor", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HtmlEditor_Renders_ShowToolbar_True()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.Services.AddScoped<DialogService>();
|
||||
var component = ctx.RenderComponent<RadzenHtmlEditor>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.ShowToolbar, true);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-html-editor-toolbar", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HtmlEditor_Renders_ShowToolbar_False()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.Services.AddScoped<DialogService>();
|
||||
var component = ctx.RenderComponent<RadzenHtmlEditor>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.ShowToolbar, false);
|
||||
});
|
||||
|
||||
Assert.DoesNotContain("rz-html-editor-toolbar", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HtmlEditor_Renders_Mode_Design()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.Services.AddScoped<DialogService>();
|
||||
var component = ctx.RenderComponent<RadzenHtmlEditor>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Mode, HtmlEditorMode.Design);
|
||||
});
|
||||
|
||||
// Design mode shows the content editable div
|
||||
Assert.Contains("contenteditable", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HtmlEditor_Renders_Mode_Source()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.Services.AddScoped<DialogService>();
|
||||
var component = ctx.RenderComponent<RadzenHtmlEditor>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Mode, HtmlEditorMode.Source);
|
||||
});
|
||||
|
||||
// Source mode shows the textarea for HTML editing
|
||||
Assert.Contains("rz-html-editor-source", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HtmlEditor_Renders_Disabled_Attribute()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.Services.AddScoped<DialogService>();
|
||||
var component = ctx.RenderComponent<RadzenHtmlEditor>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Disabled, true);
|
||||
});
|
||||
|
||||
Assert.Contains("disabled", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HtmlEditor_Renders_ContentArea()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.Services.AddScoped<DialogService>();
|
||||
var component = ctx.RenderComponent<RadzenHtmlEditor>();
|
||||
|
||||
Assert.Contains("rz-html-editor-content", component.Markup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
197
Radzen.Blazor.Tests/ListBoxTests.cs
Normal file
197
Radzen.Blazor.Tests/ListBoxTests.cs
Normal file
@@ -0,0 +1,197 @@
|
||||
using Bunit;
|
||||
using Xunit;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class ListBoxTests
|
||||
{
|
||||
class Item
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ListBox_Renders_WithClassName()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenListBox<int>>();
|
||||
|
||||
Assert.Contains(@"rz-listbox", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ListBox_Renders_WithData_SimpleList()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var data = new List<string> { "Apple", "Banana", "Cherry" };
|
||||
|
||||
var component = ctx.RenderComponent<RadzenListBox<string>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Data, data);
|
||||
});
|
||||
|
||||
Assert.Contains("Apple", component.Markup);
|
||||
Assert.Contains("Banana", component.Markup);
|
||||
Assert.Contains("Cherry", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ListBox_Renders_WithData_CustomTextValueProperties()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var data = new List<Item>
|
||||
{
|
||||
new Item { Id = 1, Name = "First Item" },
|
||||
new Item { Id = 2, Name = "Second Item" }
|
||||
};
|
||||
|
||||
var component = ctx.RenderComponent<RadzenListBox<int>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Data, data);
|
||||
parameters.Add(p => p.TextProperty, "Name");
|
||||
parameters.Add(p => p.ValueProperty, "Id");
|
||||
});
|
||||
|
||||
Assert.Contains("First Item", component.Markup);
|
||||
Assert.Contains("Second Item", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ListBox_Renders_AllowFiltering()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenListBox<int>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowFiltering, true);
|
||||
parameters.Add(p => p.Data, new List<string> { "Item1", "Item2" });
|
||||
});
|
||||
|
||||
Assert.Contains("rz-listbox-filter", component.Markup);
|
||||
Assert.Contains("rz-listbox-header", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ListBox_Renders_Disabled_Attribute()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenListBox<int>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Disabled, true);
|
||||
});
|
||||
|
||||
Assert.Contains("disabled", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ListBox_Renders_Multiple_WithSelectAll()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenListBox<IEnumerable<int>>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Multiple, true);
|
||||
parameters.Add(p => p.SelectAllText, "Select All Items");
|
||||
parameters.Add(p => p.Data, new List<string> { "Item1", "Item2" });
|
||||
});
|
||||
|
||||
Assert.Contains("Select All Items", component.Markup);
|
||||
Assert.Contains("rz-chkbox", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ListBox_Renders_FilterPlaceholder()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenListBox<int>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Placeholder, "Select an item");
|
||||
parameters.Add(p => p.AllowFiltering, true);
|
||||
parameters.Add(p => p.Data, new List<string> { "Item1", "Item2" });
|
||||
});
|
||||
|
||||
Assert.Contains("Select an item", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ListBox_Renders_Multiple_WithCheckboxes()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var data = new List<string> { "Item1", "Item2" };
|
||||
|
||||
var component = ctx.RenderComponent<RadzenListBox<IEnumerable<string>>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Multiple, true);
|
||||
parameters.Add(p => p.Data, data);
|
||||
});
|
||||
|
||||
// Multiple selection shows checkboxes in header
|
||||
Assert.Contains("rz-listbox-header-w-checkbox", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ListBox_Renders_ReadOnly()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenListBox<int>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.ReadOnly, true);
|
||||
});
|
||||
|
||||
Assert.Contains("readonly", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ListBox_Renders_TabIndex()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenListBox<int>>();
|
||||
|
||||
Assert.Contains("tabindex=", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ListBox_Renders_ListWrapper()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var data = new List<string> { "Item1" };
|
||||
|
||||
var component = ctx.RenderComponent<RadzenListBox<string>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Data, data);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-listbox-list-wrapper", component.Markup);
|
||||
Assert.Contains("rz-listbox-list", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ListBox_Renders_SearchAriaLabel()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenListBox<int>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowFiltering, true);
|
||||
parameters.Add(p => p.SearchAriaLabel, "Search items");
|
||||
parameters.Add(p => p.Data, new List<string> { "Item1" });
|
||||
});
|
||||
|
||||
Assert.Contains("aria-label=\"Search items\"", component.Markup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -141,4 +141,28 @@ foo <!---> foo -->
|
||||
{
|
||||
Assert.Equal(expected, ToXml(markdown));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("***foo bar***", @"<document>
|
||||
<paragraph>
|
||||
<emph>
|
||||
<strong>
|
||||
<text>foo bar</text>
|
||||
</strong>
|
||||
</emph>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData("___foo bar___", @"<document>
|
||||
<paragraph>
|
||||
<emph>
|
||||
<strong>
|
||||
<text>foo bar</text>
|
||||
</strong>
|
||||
</emph>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
public void Parse_BoldAndItalic_ShouldNotThrowException(string markdown, string expected)
|
||||
{
|
||||
Assert.Equal(expected, ToXml(markdown));
|
||||
}
|
||||
}
|
||||
|
||||
65
Radzen.Blazor.Tests/MediaQueryTests.cs
Normal file
65
Radzen.Blazor.Tests/MediaQueryTests.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
using Bunit;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class MediaQueryTests
|
||||
{
|
||||
[Fact]
|
||||
public void MediaQuery_Renders()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
|
||||
var component = ctx.RenderComponent<RadzenMediaQuery>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Query, "(max-width: 768px)");
|
||||
});
|
||||
|
||||
Assert.NotNull(component.Instance);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MediaQuery_HasQueryParameter()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
|
||||
var query = "(max-width: 1024px)";
|
||||
var component = ctx.RenderComponent<RadzenMediaQuery>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Query, query);
|
||||
});
|
||||
|
||||
Assert.Equal(query, component.Instance.Query);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MediaQuery_InvokesChangeCallback()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
|
||||
bool changeInvoked = false;
|
||||
bool matchResult = false;
|
||||
|
||||
var component = ctx.RenderComponent<RadzenMediaQuery>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Query, "(max-width: 768px)");
|
||||
parameters.Add(p => p.Change, EventCallback.Factory.Create<bool>(this, (matches) =>
|
||||
{
|
||||
changeInvoked = true;
|
||||
matchResult = matches;
|
||||
}));
|
||||
});
|
||||
|
||||
// Invoke the JSInvokable method directly
|
||||
component.Instance.OnChange(true);
|
||||
|
||||
Assert.True(changeInvoked);
|
||||
Assert.True(matchResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
58
Radzen.Blazor.Tests/MenuTests.cs
Normal file
58
Radzen.Blazor.Tests/MenuTests.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
using Bunit;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class MenuTests
|
||||
{
|
||||
[Fact]
|
||||
public void Menu_Renders_WithClassName()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenMenu>();
|
||||
|
||||
Assert.Contains(@"rz-menu", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Menu_Renders_Responsive_True()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenMenu>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Responsive, true);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-menu-closed", component.Markup);
|
||||
Assert.Contains("rz-menu-toggle", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Menu_Renders_Responsive_False()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenMenu>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Responsive, false);
|
||||
});
|
||||
|
||||
Assert.DoesNotContain("rz-menu-toggle", component.Markup);
|
||||
Assert.DoesNotContain("rz-menu-closed", component.Markup);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void Menu_Renders_CustomToggleAriaLabel()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenMenu>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Responsive, true);
|
||||
parameters.Add(p => p.ToggleAriaLabel, "Open navigation");
|
||||
});
|
||||
|
||||
Assert.Contains("Open navigation", component.Markup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ using Bunit;
|
||||
using Bunit.JSInterop;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
@@ -54,7 +55,7 @@ namespace Radzen.Blazor.Tests
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async void RadzenPager_Renders_Summary() {
|
||||
public async Task RadzenPager_Renders_Summary() {
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
@@ -64,7 +65,7 @@ namespace Radzen.Blazor.Tests
|
||||
parameters.Add<int>(p => p.Count, 100);
|
||||
parameters.Add<bool>(p => p.ShowPagingSummary, true);
|
||||
});
|
||||
await component.Instance.GoToPage(2);
|
||||
await component.InvokeAsync(() => component.Instance.GoToPage(2));
|
||||
component.Render();
|
||||
|
||||
Assert.Contains(@$"rz-pager-summary", component.Markup);
|
||||
@@ -111,7 +112,7 @@ namespace Radzen.Blazor.Tests
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async void RadzenPager_First_And_Prev_Buttons_Are_Disabled_When_On_The_First_Page()
|
||||
public async Task RadzenPager_First_And_Prev_Buttons_Are_Disabled_When_On_The_First_Page()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
@@ -123,7 +124,7 @@ namespace Radzen.Blazor.Tests
|
||||
parameters.Add<bool>(p => p.ShowPagingSummary, true);
|
||||
});
|
||||
|
||||
await component.Instance.GoToPage(0);
|
||||
await component.InvokeAsync(() => component.Instance.GoToPage(0));
|
||||
component.Render();
|
||||
|
||||
var firstPageButton = component.Find("a.rz-pager-first");
|
||||
@@ -134,7 +135,7 @@ namespace Radzen.Blazor.Tests
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async void RadzenPager_Last_And_Next_Buttons_Are_Disabled_When_On_The_Last_Page()
|
||||
public async Task RadzenPager_Last_And_Next_Buttons_Are_Disabled_When_On_The_Last_Page()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
@@ -146,7 +147,7 @@ namespace Radzen.Blazor.Tests
|
||||
parameters.Add<bool>(p => p.ShowPagingSummary, true);
|
||||
});
|
||||
|
||||
await component.Instance.GoToPage(9);
|
||||
await component.InvokeAsync(() => component.Instance.GoToPage(9));
|
||||
component.Render();
|
||||
|
||||
var lastPageButton = component.Find("a.rz-pager-last");
|
||||
|
||||
@@ -171,24 +171,49 @@ namespace Radzen.Blazor.Tests
|
||||
|
||||
Assert.Contains(@$"autofocus", component.Markup);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void Password_Raises_ChangedEvent()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
|
||||
var component = ctx.RenderComponent<RadzenPassword>();
|
||||
|
||||
var raised = false;
|
||||
var hasRaised = false;
|
||||
var value = "Test";
|
||||
object newValue = null;
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Change, args => { raised = true; newValue = args; }));
|
||||
var component = ctx.RenderComponent<RadzenPassword>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Change, args => { hasRaised = true; newValue = args; });
|
||||
parameters.Add(p => p.Immediate, false);
|
||||
});
|
||||
|
||||
component.Find("input").Change(value);
|
||||
var inputElement = component.Find("input");
|
||||
inputElement.Change(value);
|
||||
|
||||
Assert.True(raised);
|
||||
Assert.True(object.Equals(value, newValue));
|
||||
Assert.DoesNotContain("oninput", inputElement.ToMarkup());
|
||||
Assert.True(hasRaised);
|
||||
Assert.Equal(value, newValue);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Password_Raises_InputEvent()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var hasRaised = false;
|
||||
var value = "Test";
|
||||
object newValue = null;
|
||||
|
||||
var component = ctx.RenderComponent<RadzenPassword>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Change, args => { hasRaised = true; newValue = args; });
|
||||
parameters.Add(p => p.Immediate, true);
|
||||
});
|
||||
|
||||
var inputElement = component.Find("input");
|
||||
inputElement.Input(value);
|
||||
|
||||
Assert.DoesNotContain("onchange", inputElement.ToMarkup());
|
||||
Assert.True(hasRaised);
|
||||
Assert.Equal(value, newValue);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
220
Radzen.Blazor.Tests/PickListTests.cs
Normal file
220
Radzen.Blazor.Tests/PickListTests.cs
Normal file
@@ -0,0 +1,220 @@
|
||||
using Bunit;
|
||||
using Xunit;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class PickListTests
|
||||
{
|
||||
class Item
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PickList_Renders_WithClassName()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenPickList<Item>>();
|
||||
|
||||
Assert.Contains(@"rz-picklist", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PickList_Renders_SourceAndTargetLists()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenPickList<Item>>();
|
||||
|
||||
Assert.Contains("rz-picklist-source-wrapper", component.Markup);
|
||||
Assert.Contains("rz-picklist-target-wrapper", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PickList_Renders_TransferButtons()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenPickList<Item>>();
|
||||
|
||||
Assert.Contains("rz-picklist-buttons", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PickList_Renders_Orientation_Horizontal()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenPickList<Item>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Orientation, Orientation.Horizontal);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-flex-row", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PickList_Renders_Orientation_Vertical()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenPickList<Item>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Orientation, Orientation.Vertical);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-flex-column", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PickList_Renders_AllowFiltering()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var data = new List<Item>
|
||||
{
|
||||
new Item { Id = 1, Name = "Item 1" }
|
||||
};
|
||||
|
||||
var component = ctx.RenderComponent<RadzenPickList<Item>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowFiltering, true);
|
||||
parameters.Add(p => p.Source, data);
|
||||
parameters.Add(p => p.TextProperty, "Name");
|
||||
});
|
||||
|
||||
Assert.Contains("rz-listbox-filter", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PickList_Renders_SourceData()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var data = new List<Item>
|
||||
{
|
||||
new Item { Id = 1, Name = "Source Item 1" },
|
||||
new Item { Id = 2, Name = "Source Item 2" }
|
||||
};
|
||||
|
||||
var component = ctx.RenderComponent<RadzenPickList<Item>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Source, data);
|
||||
parameters.Add(p => p.TextProperty, "Name");
|
||||
});
|
||||
|
||||
Assert.Contains("Source Item 1", component.Markup);
|
||||
Assert.Contains("Source Item 2", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PickList_Renders_ShowHeader()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenPickList<Item>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.ShowHeader, true);
|
||||
parameters.Add(p => p.SourceHeader, builder => builder.AddContent(0, "Available Items"));
|
||||
parameters.Add(p => p.TargetHeader, builder => builder.AddContent(0, "Selected Items"));
|
||||
});
|
||||
|
||||
Assert.Contains("Available Items", component.Markup);
|
||||
Assert.Contains("Selected Items", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PickList_Renders_AllowMoveAll_Buttons()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var data = new List<Item> { new Item { Id = 1, Name = "Item" } };
|
||||
|
||||
var component = ctx.RenderComponent<RadzenPickList<Item>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowMoveAll, true);
|
||||
parameters.Add(p => p.Source, data);
|
||||
});
|
||||
|
||||
// Should have 4 buttons when AllowMoveAll is true
|
||||
var buttonCount = System.Text.RegularExpressions.Regex.Matches(component.Markup, "rz-button").Count;
|
||||
Assert.True(buttonCount >= 4);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PickList_Renders_Disabled()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenPickList<Item>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Disabled, true);
|
||||
});
|
||||
|
||||
Assert.Contains("disabled", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PickList_GetSelectedSources_Respects_ValueProperty_Single()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
|
||||
var data = new List<Item>
|
||||
{
|
||||
new Item { Id = 1, Name = "A" },
|
||||
new Item { Id = 2, Name = "B" }
|
||||
};
|
||||
|
||||
var component = ctx.RenderComponent<RadzenPickList<Item>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Source, data);
|
||||
parameters.Add(p => p.TextProperty, "Name");
|
||||
parameters.Add(p => p.ValueProperty, "Id");
|
||||
parameters.Add(p => p.Multiple, false);
|
||||
});
|
||||
|
||||
// Simulate ListBox selection when ValueProperty is set: selectedSourceItems becomes the value (Id)
|
||||
var field = typeof(RadzenPickList<Item>).GetField("selectedSourceItems", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
|
||||
field.SetValue(component.Instance, 2);
|
||||
|
||||
var selected = component.Instance.GetSelectedSources();
|
||||
Assert.Single(selected);
|
||||
Assert.Equal(2, selected.First().Id);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PickList_GetSelectedSources_Respects_ValueProperty_Multiple()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
|
||||
var data = new List<Item>
|
||||
{
|
||||
new Item { Id = 1, Name = "A" },
|
||||
new Item { Id = 2, Name = "B" },
|
||||
new Item { Id = 3, Name = "C" }
|
||||
};
|
||||
|
||||
var component = ctx.RenderComponent<RadzenPickList<Item>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Source, data);
|
||||
parameters.Add(p => p.TextProperty, "Name");
|
||||
parameters.Add(p => p.ValueProperty, "Id");
|
||||
parameters.Add(p => p.Multiple, true);
|
||||
});
|
||||
|
||||
// Simulate ListBox selection when ValueProperty is set: selectedSourceItems becomes IEnumerable of values (Ids)
|
||||
var field = typeof(RadzenPickList<Item>).GetField("selectedSourceItems", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
|
||||
field.SetValue(component.Instance, new[] { 1, 3 });
|
||||
|
||||
var selected = component.Instance.GetSelectedSources().Select(i => i.Id).OrderBy(i => i).ToArray();
|
||||
Assert.Equal(new[] { 1, 3 }, selected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
644
Radzen.Blazor.Tests/PivotDataGridTests.cs
Normal file
644
Radzen.Blazor.Tests/PivotDataGridTests.cs
Normal file
@@ -0,0 +1,644 @@
|
||||
using Bunit;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Radzen;
|
||||
using Radzen.Blazor;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class PivotDataGridTests
|
||||
{
|
||||
private static readonly List<SalesData> SampleData = new()
|
||||
{
|
||||
new SalesData { Region = "North", Category = "Electronics", Product = "Laptop", Amount = 1000, Year = 2023 },
|
||||
new SalesData { Region = "North", Category = "Electronics", Product = "Laptop", Amount = 1500, Year = 2024 },
|
||||
new SalesData { Region = "South", Category = "Home", Product = "Vacuum", Amount = 500, Year = 2023 }
|
||||
};
|
||||
|
||||
public class SalesData
|
||||
{
|
||||
public string Region { get; set; }
|
||||
public string Category { get; set; }
|
||||
public string Product { get; set; }
|
||||
public double Amount { get; set; }
|
||||
public int Year { get; set; }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PivotDataGrid_Renders_CssClasses()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = RenderPivotDataGrid(ctx);
|
||||
|
||||
component.WaitForAssertion(() =>
|
||||
{
|
||||
Assert.Contains("rz-pivot-data-grid", component.Markup);
|
||||
Assert.Contains("rz-pivot-table", component.Markup);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PivotDataGrid_Renders_RowAndColumnHeaders()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = RenderPivotDataGrid(ctx);
|
||||
|
||||
component.WaitForAssertion(() =>
|
||||
{
|
||||
var table = component.Find(".rz-pivot-content .rz-pivot-table");
|
||||
var headers = table.GetElementsByClassName("rz-pivot-header-text").Select(h => h.TextContent.Trim()).ToList();
|
||||
var aggregateHeaders = table.GetElementsByClassName("rz-pivot-aggregate-header").Select(h => h.TextContent.Trim()).ToList();
|
||||
|
||||
Assert.Contains("Region", headers);
|
||||
Assert.Contains("2023", headers);
|
||||
Assert.Contains("Sales", aggregateHeaders);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PivotDataGrid_AllowSorting_RendersSortableClass()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = RenderPivotDataGrid(ctx, parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowSorting, true);
|
||||
});
|
||||
|
||||
component.WaitForAssertion(() =>
|
||||
{
|
||||
var sortableHeaders = component.FindAll(".rz-pivot-header-content.rz-sortable");
|
||||
Assert.NotEmpty(sortableHeaders);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PivotDataGrid_AllowFiltering_RendersFilterIcon()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = RenderPivotDataGrid(ctx, parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowFiltering, true);
|
||||
});
|
||||
|
||||
component.WaitForAssertion(() =>
|
||||
{
|
||||
var filterIcons = component.FindAll(".rz-grid-filter-icon");
|
||||
Assert.NotEmpty(filterIcons);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PivotDataGrid_Renders_AggregateValues()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = RenderPivotDataGrid(ctx);
|
||||
|
||||
component.WaitForAssertion(() =>
|
||||
{
|
||||
Assert.Contains("1000", component.Markup);
|
||||
Assert.Contains("1500", component.Markup);
|
||||
Assert.Contains("500", component.Markup);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PivotDataGrid_DisallowFiltering_HidesFilterIcon()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = RenderPivotDataGrid(ctx, parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowFiltering, false);
|
||||
});
|
||||
|
||||
component.WaitForAssertion(() =>
|
||||
{
|
||||
var filterIcons = component.FindAll(".rz-grid-filter-icon");
|
||||
Assert.Empty(filterIcons);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PivotDataGrid_DisallowSorting_HidesSortableClass()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = RenderPivotDataGrid(ctx, parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowSorting, false);
|
||||
});
|
||||
|
||||
component.WaitForAssertion(() =>
|
||||
{
|
||||
var sortableHeaders = component.FindAll(".rz-pivot-header-content.rz-sortable");
|
||||
Assert.Empty(sortableHeaders);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PivotDataGrid_ShowColumnsTotals_RendersFooter()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = RenderPivotDataGrid(ctx, parameters =>
|
||||
{
|
||||
parameters.Add(p => p.ShowColumnsTotals, true);
|
||||
});
|
||||
|
||||
component.WaitForAssertion(() =>
|
||||
{
|
||||
var footer = component.FindAll(".rz-pivot-footer");
|
||||
Assert.NotEmpty(footer);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PivotDataGrid_HideColumnsTotals_NoFooter()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = RenderPivotDataGrid(ctx, parameters =>
|
||||
{
|
||||
parameters.Add(p => p.ShowColumnsTotals, false);
|
||||
});
|
||||
|
||||
component.WaitForAssertion(() =>
|
||||
{
|
||||
var footer = component.FindAll(".rz-pivot-footer");
|
||||
Assert.Empty(footer);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PivotDataGrid_Renders_DefaultEmptyText()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenPivotDataGrid<SalesData>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Data, new List<SalesData>());
|
||||
parameters.Add(p => p.AllowFieldsPicking, false);
|
||||
});
|
||||
|
||||
component.WaitForAssertion(() =>
|
||||
{
|
||||
Assert.Contains("No records to display.", component.Markup);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PivotDataGrid_Renders_CustomEmptyText()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenPivotDataGrid<SalesData>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Data, new List<SalesData>());
|
||||
parameters.Add(p => p.EmptyText, "No data available");
|
||||
parameters.Add(p => p.AllowFieldsPicking, false);
|
||||
});
|
||||
|
||||
component.WaitForAssertion(() =>
|
||||
{
|
||||
Assert.Contains("No data available", component.Markup);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PivotDataGrid_AllowPaging_RendersPager()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = RenderPivotDataGrid(ctx, parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowPaging, true);
|
||||
parameters.Add(p => p.PageSize, 2);
|
||||
});
|
||||
|
||||
component.WaitForAssertion(() =>
|
||||
{
|
||||
Assert.Contains("rz-pager", component.Markup);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PivotDataGrid_DisallowPaging_HidesPager()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = RenderPivotDataGrid(ctx, parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowPaging, false);
|
||||
});
|
||||
|
||||
component.WaitForAssertion(() =>
|
||||
{
|
||||
Assert.DoesNotContain("rz-pager", component.Markup);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PivotDataGrid_PagerPosition_Top()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = RenderPivotDataGrid(ctx, parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowPaging, true);
|
||||
parameters.Add(p => p.PagerPosition, PagerPosition.Top);
|
||||
parameters.Add(p => p.PageSize, 2);
|
||||
});
|
||||
|
||||
component.WaitForAssertion(() =>
|
||||
{
|
||||
Assert.Contains("rz-pager", component.Markup);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PivotDataGrid_PagerPosition_TopAndBottom()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = RenderPivotDataGrid(ctx, parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowPaging, true);
|
||||
parameters.Add(p => p.PagerPosition, PagerPosition.TopAndBottom);
|
||||
parameters.Add(p => p.PageSize, 2);
|
||||
});
|
||||
|
||||
component.WaitForAssertion(() =>
|
||||
{
|
||||
var pagers = component.FindAll(".rz-pager");
|
||||
Assert.True(pagers.Count >= 1); // Should have at least one pager
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PivotDataGrid_Density_Compact()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = RenderPivotDataGrid(ctx, parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowPaging, true);
|
||||
parameters.Add(p => p.PageSize, 1); // Force pager to show with small page size
|
||||
parameters.Add(p => p.AllowFieldsPicking, false);
|
||||
parameters.Add(p => p.Density, Density.Compact);
|
||||
});
|
||||
|
||||
component.WaitForAssertion(() =>
|
||||
{
|
||||
Assert.Contains("rz-density-compact", component.Markup);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PivotDataGrid_AllowAlternatingRows_True()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = RenderPivotDataGrid(ctx, parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowAlternatingRows, true);
|
||||
});
|
||||
|
||||
component.WaitForAssertion(() =>
|
||||
{
|
||||
Assert.Contains("rz-grid-table-striped", component.Markup);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PivotDataGrid_AllowAlternatingRows_False()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = RenderPivotDataGrid(ctx, parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowAlternatingRows, false);
|
||||
});
|
||||
|
||||
component.WaitForAssertion(() =>
|
||||
{
|
||||
Assert.DoesNotContain("rz-grid-table-striped", component.Markup);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PivotDataGrid_AllowFieldsPicking_ShowsPanel()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = RenderPivotDataGrid(ctx, parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowFieldsPicking, true);
|
||||
});
|
||||
|
||||
component.WaitForAssertion(() =>
|
||||
{
|
||||
Assert.Contains("rz-panel", component.Markup);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PivotDataGrid_AllowFieldsPicking_False_HidesPanel()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = RenderPivotDataGrid(ctx, parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowFieldsPicking, false);
|
||||
});
|
||||
|
||||
component.WaitForAssertion(() =>
|
||||
{
|
||||
Assert.DoesNotContain("rz-panel", component.Markup);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PivotDataGrid_Renders_AllowDrillDown()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = RenderPivotDataGrid(ctx, parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowDrillDown, true);
|
||||
});
|
||||
|
||||
component.WaitForAssertion(() =>
|
||||
{
|
||||
// Should render pivot content
|
||||
Assert.Contains("rz-pivot-content", component.Markup);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PivotDataGrid_Renders_RowValues()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = RenderPivotDataGrid(ctx);
|
||||
|
||||
component.WaitForAssertion(() =>
|
||||
{
|
||||
var cells = component.FindAll(".rz-pivot-row-header");
|
||||
var cellTexts = cells.Select(c => c.TextContent.Trim()).ToList();
|
||||
|
||||
Assert.Contains("North", cellTexts);
|
||||
Assert.Contains("South", cellTexts);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PivotDataGrid_Renders_MultipleRows()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenPivotDataGrid<SalesData>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Data, SampleData);
|
||||
|
||||
parameters.Add<RenderFragment>(p => p.Rows, builder =>
|
||||
{
|
||||
builder.OpenComponent<RadzenPivotRow<SalesData>>(0);
|
||||
builder.AddAttribute(1, nameof(RadzenPivotRow<SalesData>.Property), nameof(SalesData.Region));
|
||||
builder.AddAttribute(2, nameof(RadzenPivotRow<SalesData>.Title), "Region");
|
||||
builder.CloseComponent();
|
||||
|
||||
builder.OpenComponent<RadzenPivotRow<SalesData>>(2);
|
||||
builder.AddAttribute(3, nameof(RadzenPivotRow<SalesData>.Property), nameof(SalesData.Category));
|
||||
builder.AddAttribute(4, nameof(RadzenPivotRow<SalesData>.Title), "Category");
|
||||
builder.CloseComponent();
|
||||
});
|
||||
|
||||
parameters.Add<RenderFragment>(p => p.Columns, builder =>
|
||||
{
|
||||
builder.OpenComponent<RadzenPivotColumn<SalesData>>(0);
|
||||
builder.AddAttribute(1, nameof(RadzenPivotColumn<SalesData>.Property), nameof(SalesData.Year));
|
||||
builder.AddAttribute(2, nameof(RadzenPivotColumn<SalesData>.Title), "Year");
|
||||
builder.CloseComponent();
|
||||
});
|
||||
|
||||
parameters.Add<RenderFragment>(p => p.Aggregates, builder =>
|
||||
{
|
||||
builder.OpenComponent<RadzenPivotAggregate<SalesData>>(0);
|
||||
builder.AddAttribute(1, nameof(RadzenPivotAggregate<SalesData>.Property), nameof(SalesData.Amount));
|
||||
builder.AddAttribute(2, nameof(RadzenPivotAggregate<SalesData>.Title), "Sales");
|
||||
builder.AddAttribute(3, nameof(RadzenPivotAggregate<SalesData>.Aggregate), AggregateFunction.Sum);
|
||||
builder.CloseComponent();
|
||||
});
|
||||
});
|
||||
|
||||
component.WaitForAssertion(() =>
|
||||
{
|
||||
var headers = component.FindAll(".rz-pivot-header-text").Select(h => h.TextContent.Trim()).ToList();
|
||||
Assert.Contains("Region", headers);
|
||||
Assert.Contains("Category", headers);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PivotDataGrid_Renders_MultipleAggregates()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenPivotDataGrid<SalesData>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Data, SampleData);
|
||||
|
||||
parameters.Add<RenderFragment>(p => p.Rows, builder =>
|
||||
{
|
||||
builder.OpenComponent<RadzenPivotRow<SalesData>>(0);
|
||||
builder.AddAttribute(1, nameof(RadzenPivotRow<SalesData>.Property), nameof(SalesData.Region));
|
||||
builder.AddAttribute(2, nameof(RadzenPivotRow<SalesData>.Title), "Region");
|
||||
builder.CloseComponent();
|
||||
});
|
||||
|
||||
parameters.Add<RenderFragment>(p => p.Columns, builder =>
|
||||
{
|
||||
builder.OpenComponent<RadzenPivotColumn<SalesData>>(0);
|
||||
builder.AddAttribute(1, nameof(RadzenPivotColumn<SalesData>.Property), nameof(SalesData.Year));
|
||||
builder.AddAttribute(2, nameof(RadzenPivotColumn<SalesData>.Title), "Year");
|
||||
builder.CloseComponent();
|
||||
});
|
||||
|
||||
parameters.Add<RenderFragment>(p => p.Aggregates, builder =>
|
||||
{
|
||||
builder.OpenComponent<RadzenPivotAggregate<SalesData>>(0);
|
||||
builder.AddAttribute(1, nameof(RadzenPivotAggregate<SalesData>.Property), nameof(SalesData.Amount));
|
||||
builder.AddAttribute(2, nameof(RadzenPivotAggregate<SalesData>.Title), "Total Sales");
|
||||
builder.AddAttribute(3, nameof(RadzenPivotAggregate<SalesData>.Aggregate), AggregateFunction.Sum);
|
||||
builder.CloseComponent();
|
||||
|
||||
builder.OpenComponent<RadzenPivotAggregate<SalesData>>(4);
|
||||
builder.AddAttribute(5, nameof(RadzenPivotAggregate<SalesData>.Property), nameof(SalesData.Amount));
|
||||
builder.AddAttribute(6, nameof(RadzenPivotAggregate<SalesData>.Title), "Count Sales");
|
||||
builder.AddAttribute(7, nameof(RadzenPivotAggregate<SalesData>.Aggregate), AggregateFunction.Count);
|
||||
builder.CloseComponent();
|
||||
});
|
||||
});
|
||||
|
||||
component.WaitForAssertion(() =>
|
||||
{
|
||||
var aggregateHeaders = component.FindAll(".rz-pivot-aggregate-header").Select(h => h.TextContent.Trim()).ToList();
|
||||
Assert.Contains("Total Sales", aggregateHeaders);
|
||||
Assert.Contains("Count Sales", aggregateHeaders);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PivotDataGrid_Renders_AlternatingRowClasses()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = RenderPivotDataGrid(ctx, parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowAlternatingRows, true);
|
||||
});
|
||||
|
||||
component.WaitForAssertion(() =>
|
||||
{
|
||||
Assert.Contains("rz-pivot-row-even", component.Markup);
|
||||
Assert.Contains("rz-pivot-row-odd", component.Markup);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PivotDataGrid_PageSize_DefaultsTo10()
|
||||
{
|
||||
var grid = new RadzenPivotDataGrid<SalesData>();
|
||||
Assert.Equal(10, grid.PageSize);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PivotDataGrid_AllowSorting_DefaultsToTrue()
|
||||
{
|
||||
var grid = new RadzenPivotDataGrid<SalesData>();
|
||||
Assert.True(grid.AllowSorting);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PivotDataGrid_AllowFiltering_DefaultsToTrue()
|
||||
{
|
||||
var grid = new RadzenPivotDataGrid<SalesData>();
|
||||
Assert.True(grid.AllowFiltering);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PivotDataGrid_AllowAlternatingRows_DefaultsToTrue()
|
||||
{
|
||||
var grid = new RadzenPivotDataGrid<SalesData>();
|
||||
Assert.True(grid.AllowAlternatingRows);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PivotDataGrid_AllowDrillDown_DefaultsToTrue()
|
||||
{
|
||||
var grid = new RadzenPivotDataGrid<SalesData>();
|
||||
Assert.True(grid.AllowDrillDown);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PivotDataGrid_AllowFieldsPicking_DefaultsToTrue()
|
||||
{
|
||||
var grid = new RadzenPivotDataGrid<SalesData>();
|
||||
Assert.True(grid.AllowFieldsPicking);
|
||||
}
|
||||
|
||||
private static IRenderedComponent<RadzenPivotDataGrid<SalesData>> RenderPivotDataGrid(TestContext ctx, Action<ComponentParameterCollectionBuilder<RadzenPivotDataGrid<SalesData>>> configure = null)
|
||||
{
|
||||
return ctx.RenderComponent<RadzenPivotDataGrid<SalesData>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Data, SampleData);
|
||||
|
||||
parameters.Add<RenderFragment>(p => p.Rows, builder =>
|
||||
{
|
||||
builder.OpenComponent<RadzenPivotRow<SalesData>>(0);
|
||||
builder.AddAttribute(1, nameof(RadzenPivotRow<SalesData>.Property), nameof(SalesData.Region));
|
||||
builder.AddAttribute(2, nameof(RadzenPivotRow<SalesData>.Title), "Region");
|
||||
builder.CloseComponent();
|
||||
});
|
||||
|
||||
parameters.Add<RenderFragment>(p => p.Columns, builder =>
|
||||
{
|
||||
builder.OpenComponent<RadzenPivotColumn<SalesData>>(0);
|
||||
builder.AddAttribute(1, nameof(RadzenPivotColumn<SalesData>.Property), nameof(SalesData.Year));
|
||||
builder.AddAttribute(2, nameof(RadzenPivotColumn<SalesData>.Title), "Year");
|
||||
builder.CloseComponent();
|
||||
});
|
||||
|
||||
parameters.Add<RenderFragment>(p => p.Aggregates, builder =>
|
||||
{
|
||||
builder.OpenComponent<RadzenPivotAggregate<SalesData>>(0);
|
||||
builder.AddAttribute(1, nameof(RadzenPivotAggregate<SalesData>.Property), nameof(SalesData.Amount));
|
||||
builder.AddAttribute(2, nameof(RadzenPivotAggregate<SalesData>.Title), "Sales");
|
||||
builder.AddAttribute(3, nameof(RadzenPivotAggregate<SalesData>.Aggregate), AggregateFunction.Sum);
|
||||
builder.CloseComponent();
|
||||
});
|
||||
|
||||
configure?.Invoke(parameters);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
120
Radzen.Blazor.Tests/ProfileMenuItemTests.cs
Normal file
120
Radzen.Blazor.Tests/ProfileMenuItemTests.cs
Normal file
@@ -0,0 +1,120 @@
|
||||
using Bunit;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class ProfileMenuItemTests
|
||||
{
|
||||
[Fact]
|
||||
public void ProfileMenuItem_Renders_TextParameter()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
|
||||
var component = ctx.RenderComponent<RadzenProfileMenu>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.ChildContent, builder =>
|
||||
{
|
||||
builder.OpenComponent<RadzenProfileMenuItem>(0);
|
||||
builder.AddAttribute(1, "Text", "Profile");
|
||||
builder.CloseComponent();
|
||||
});
|
||||
});
|
||||
|
||||
Assert.Contains("Profile", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProfileMenuItem_Renders_IconParameter()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
|
||||
var component = ctx.RenderComponent<RadzenProfileMenu>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.ChildContent, builder =>
|
||||
{
|
||||
builder.OpenComponent<RadzenProfileMenuItem>(0);
|
||||
builder.AddAttribute(1, "Icon", "account_circle");
|
||||
builder.AddAttribute(2, "Text", "Profile");
|
||||
builder.CloseComponent();
|
||||
});
|
||||
});
|
||||
|
||||
Assert.Contains("account_circle", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProfileMenuItem_Template_OverridesText()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
|
||||
var component = ctx.RenderComponent<RadzenProfileMenu>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.ChildContent, builder =>
|
||||
{
|
||||
builder.OpenComponent<RadzenProfileMenuItem>(0);
|
||||
builder.AddAttribute(1, "Text", "This should not appear");
|
||||
builder.AddAttribute(2, "Template", (RenderFragment)((templateBuilder) =>
|
||||
{
|
||||
templateBuilder.OpenElement(0, "span");
|
||||
templateBuilder.AddAttribute(1, "class", "template-content");
|
||||
templateBuilder.AddContent(2, "Template Content");
|
||||
templateBuilder.CloseElement();
|
||||
}));
|
||||
builder.CloseComponent();
|
||||
});
|
||||
});
|
||||
|
||||
// Template should be rendered
|
||||
Assert.Contains("template-content", component.Markup);
|
||||
// Text should not be rendered in navigation-item-text span when Template is present
|
||||
Assert.DoesNotContain("<span class=\"rz-navigation-item-text\">This should not appear</span>", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProfileMenuItem_Renders_TemplateWithSwitch()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
|
||||
var component = ctx.RenderComponent<RadzenProfileMenu>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.ChildContent, builder =>
|
||||
{
|
||||
builder.OpenComponent<RadzenProfileMenuItem>(0);
|
||||
builder.AddAttribute(1, "Icon", "settings");
|
||||
builder.AddAttribute(2, "Template", (RenderFragment)((templateBuilder) =>
|
||||
{
|
||||
templateBuilder.OpenComponent<RadzenSwitch>(0);
|
||||
templateBuilder.CloseComponent();
|
||||
}));
|
||||
builder.CloseComponent();
|
||||
});
|
||||
});
|
||||
|
||||
// Icon should still be rendered
|
||||
Assert.Contains("settings", component.Markup);
|
||||
// Switch should be rendered from template
|
||||
Assert.Contains("rz-switch", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProfileMenuItem_Renders_PathParameter()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
|
||||
var component = ctx.RenderComponent<RadzenProfileMenu>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.ChildContent, builder =>
|
||||
{
|
||||
builder.OpenComponent<RadzenProfileMenuItem>(0);
|
||||
builder.AddAttribute(1, "Text", "Settings");
|
||||
builder.AddAttribute(2, "Path", "/settings");
|
||||
builder.CloseComponent();
|
||||
});
|
||||
});
|
||||
|
||||
Assert.Contains("href=\"/settings\"", component.Markup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,5 +142,45 @@ namespace Radzen.Blazor.Tests
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Mode, ProgressBarMode.Indeterminate));
|
||||
Assert.Contains(@$"rz-progressbar-info", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProgressBar_Renders_ShowValue_True()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenProgressBar>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.ShowValue, true);
|
||||
parameters.Add(p => p.Value, 50);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-progressbar-label", component.Markup);
|
||||
Assert.Contains("50%", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProgressBar_Renders_ShowValue_False()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenProgressBar>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.ShowValue, false);
|
||||
parameters.Add(p => p.Value, 50);
|
||||
});
|
||||
|
||||
Assert.DoesNotContain("rz-progressbar-label", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProgressBar_Renders_Template()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenProgressBar>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Value, 75);
|
||||
parameters.Add(p => p.Template, builder => builder.AddContent(0, "Custom: 75%"));
|
||||
});
|
||||
|
||||
Assert.Contains("Custom: 75%", component.Markup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Xunit;
|
||||
|
||||
@@ -114,6 +114,11 @@ namespace Radzen.Blazor.Tests
|
||||
public List<string> Values { get; set; }
|
||||
}
|
||||
|
||||
public class Order
|
||||
{
|
||||
public DateTime? OrderDate { get; set; }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetProperty_Should_Resolve_DescriptionProperty()
|
||||
{
|
||||
@@ -137,6 +142,14 @@ namespace Radzen.Blazor.Tests
|
||||
Assert.NotNull(idProperty);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetPropertyType_Resolves_NullableDateTime_Date()
|
||||
{
|
||||
var propertyType = PropertyAccess.GetPropertyType(typeof(Order), "OrderDate.Date");
|
||||
|
||||
Assert.Equal(typeof(DateTime), propertyType);
|
||||
}
|
||||
|
||||
interface ISimpleInterface : ISimpleNestedInterface
|
||||
{
|
||||
string Description { get; set; }
|
||||
|
||||
2550
Radzen.Blazor.Tests/QueryableExtensionTests.cs
Normal file
2550
Radzen.Blazor.Tests/QueryableExtensionTests.cs
Normal file
File diff suppressed because it is too large
Load Diff
62
Radzen.Blazor.Tests/RadioButtonListTests.cs
Normal file
62
Radzen.Blazor.Tests/RadioButtonListTests.cs
Normal file
@@ -0,0 +1,62 @@
|
||||
using Bunit;
|
||||
using System.Collections.Generic;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class RadioButtonListTests
|
||||
{
|
||||
[Fact]
|
||||
public void RadioButtonList_Renders_WithClassName()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenRadioButtonList<int>>();
|
||||
|
||||
Assert.Contains(@"rz-radio-button-list", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RadioButtonList_Renders_Orientation()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenRadioButtonList<int>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Items, builder =>
|
||||
{
|
||||
builder.OpenComponent<RadzenRadioButtonListItem<int>>(0);
|
||||
builder.AddAttribute(1, "Text", "Option 1");
|
||||
builder.AddAttribute(2, "Value", 1);
|
||||
builder.CloseComponent();
|
||||
});
|
||||
});
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Orientation, Orientation.Horizontal));
|
||||
// Orientation is applied via RadzenStack which uses flex-direction
|
||||
Assert.Contains("rz-flex-row", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Orientation, Orientation.Vertical));
|
||||
Assert.Contains("rz-flex-column", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RadioButtonList_Renders_Disabled()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenRadioButtonList<int>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Disabled, true);
|
||||
parameters.Add(p => p.Items, builder =>
|
||||
{
|
||||
builder.OpenComponent<RadzenRadioButtonListItem<int>>(0);
|
||||
builder.AddAttribute(1, "Text", "Option 1");
|
||||
builder.AddAttribute(2, "Value", 1);
|
||||
builder.CloseComponent();
|
||||
});
|
||||
});
|
||||
|
||||
// Disabled class is on the radio button items
|
||||
Assert.Contains("rz-state-disabled", component.Markup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9</TargetFramework>
|
||||
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<Nullable>disable</Nullable>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.1" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.0" />
|
||||
<PackageReference Include="bunit.web" Version="1.36.0" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
|
||||
|
||||
82
Radzen.Blazor.Tests/RatingTests.cs
Normal file
82
Radzen.Blazor.Tests/RatingTests.cs
Normal file
@@ -0,0 +1,82 @@
|
||||
using Bunit;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class RatingTests
|
||||
{
|
||||
[Fact]
|
||||
public void Rating_Renders_WithClassName()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenRating>();
|
||||
|
||||
Assert.Contains(@"rz-rating", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Rating_Renders_Stars()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenRating>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Stars, 5));
|
||||
|
||||
// Should render 5 star icons (rzi-star or rzi-star-o) + 1 clear button icon = 6 total
|
||||
var starCount = System.Text.RegularExpressions.Regex.Matches(component.Markup, "rz-rating-icon").Count;
|
||||
Assert.Equal(6, starCount); // 5 stars + 1 clear button
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Rating_Renders_CustomStarCount()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenRating>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Stars, 10));
|
||||
|
||||
var starCount = System.Text.RegularExpressions.Regex.Matches(component.Markup, "rz-rating-icon").Count;
|
||||
Assert.Equal(11, starCount); // 10 stars + 1 clear button
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Rating_Renders_Value()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenRating>();
|
||||
|
||||
component.SetParametersAndRender(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Value, 3);
|
||||
parameters.Add(p => p.Stars, 5);
|
||||
});
|
||||
|
||||
// Should have 3 filled stars (rzi-star) and 2 outline stars (rzi-star-o)
|
||||
var filledStars = System.Text.RegularExpressions.Regex.Matches(component.Markup, "rzi-star\"").Count;
|
||||
Assert.Equal(3, filledStars);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Rating_Renders_ReadOnly()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenRating>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.ReadOnly, true));
|
||||
|
||||
Assert.Contains("rz-state-readonly", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Rating_Renders_Disabled()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenRating>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Disabled, true));
|
||||
|
||||
Assert.Contains("rz-state-disabled", component.Markup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
63
Radzen.Blazor.Tests/RowTests.cs
Normal file
63
Radzen.Blazor.Tests/RowTests.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
using Bunit;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class RowTests
|
||||
{
|
||||
[Fact]
|
||||
public void Row_Renders_WithClassName()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenRow>();
|
||||
|
||||
Assert.Contains(@"rz-row", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Row_Renders_ChildContent()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenRow>(parameters =>
|
||||
{
|
||||
parameters.AddChildContent("<div>Row Content</div>");
|
||||
});
|
||||
|
||||
Assert.Contains("Row Content", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Row_Renders_Gap()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenRow>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Gap, "2rem"));
|
||||
|
||||
Assert.Contains("--rz-gap:2rem", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Row_Renders_RowGap()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenRow>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.RowGap, "1.5rem"));
|
||||
|
||||
Assert.Contains("--rz-row-gap:1.5rem", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Row_Renders_ColumnGap()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenRow>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Gap, "1rem"));
|
||||
|
||||
Assert.Contains("--rz-gap:1rem", component.Markup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
63
Radzen.Blazor.Tests/SchedulerYearRangeTests.cs
Normal file
63
Radzen.Blazor.Tests/SchedulerYearRangeTests.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
using Bunit;
|
||||
using Microsoft.JSInterop;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Radzen.Blazor;
|
||||
using Radzen.Blazor.Rendering;
|
||||
using Radzen;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class SchedulerYearRangeTests
|
||||
{
|
||||
class Appointment
|
||||
{
|
||||
public DateTime Start { get; set; }
|
||||
public DateTime End { get; set; }
|
||||
public string Text { get; set; } = "";
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void YearView_StartMonthJanuary_IncludesLastDaysOfYear_WhenYearStartsOnFirstDayOfWeek()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.Services.AddScoped<DialogService>();
|
||||
ctx.JSInterop.Setup<Rect>("Radzen.createScheduler", _ => true)
|
||||
.SetResult(new Rect { Left = 0, Top = 0, Width = 200, Height = 200 });
|
||||
|
||||
// Make the first day of week Monday and use a year where Jan 1 is Monday (2024-01-01).
|
||||
var culture = (CultureInfo)CultureInfo.InvariantCulture.Clone();
|
||||
culture.DateTimeFormat.FirstDayOfWeek = DayOfWeek.Monday;
|
||||
|
||||
var appointments = new List<Appointment>
|
||||
{
|
||||
new() { Start = new DateTime(2024, 12, 31), End = new DateTime(2025, 1, 1), Text = "Year end" }
|
||||
};
|
||||
|
||||
var cut = ctx.RenderComponent<RadzenScheduler<Appointment>>(p =>
|
||||
{
|
||||
p.Add(x => x.Culture, culture);
|
||||
p.Add(x => x.Date, new DateTime(2024, 6, 1));
|
||||
p.Add(x => x.Data, appointments);
|
||||
p.Add(x => x.StartProperty, nameof(Appointment.Start));
|
||||
p.Add(x => x.EndProperty, nameof(Appointment.End));
|
||||
p.Add(x => x.TextProperty, nameof(Appointment.Text));
|
||||
p.AddChildContent<RadzenYearView>(v => v.Add(x => x.StartMonth, Radzen.Month.January));
|
||||
});
|
||||
|
||||
var view = Assert.IsType<RadzenYearView>(cut.Instance.SelectedView);
|
||||
|
||||
// View should start on 2023-12-25 (one extra week above since 2024-01-01 is Monday).
|
||||
Assert.Equal(new DateTime(2023, 12, 25), view.StartDate.Date);
|
||||
|
||||
// View end must include 2024-12-31 (it should extend to end-of-week containing the real year end).
|
||||
Assert.True(view.EndDate.Date >= new DateTime(2024, 12, 31), $"EndDate was {view.EndDate:yyyy-MM-dd}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
45
Radzen.Blazor.Tests/SecurityCodeTests.cs
Normal file
45
Radzen.Blazor.Tests/SecurityCodeTests.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
using Bunit;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class SecurityCodeTests
|
||||
{
|
||||
[Fact]
|
||||
public void SecurityCode_Renders_WithClassName()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenSecurityCode>();
|
||||
|
||||
Assert.Contains(@"rz-security-code", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SecurityCode_Renders_Count()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenSecurityCode>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Count, 6));
|
||||
|
||||
// Should render 6 input boxes + 1 hidden input for form submission = 7 total
|
||||
var inputCount = System.Text.RegularExpressions.Regex.Matches(component.Markup, "<input").Count;
|
||||
Assert.Equal(7, inputCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SecurityCode_Renders_Disabled()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenSecurityCode>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Disabled, true));
|
||||
|
||||
Assert.Contains("disabled", component.Markup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
83
Radzen.Blazor.Tests/SelectBarTests.cs
Normal file
83
Radzen.Blazor.Tests/SelectBarTests.cs
Normal file
@@ -0,0 +1,83 @@
|
||||
using Bunit;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class SelectBarTests
|
||||
{
|
||||
[Fact]
|
||||
public void SelectBar_Renders_WithClassName()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenSelectBar<int>>();
|
||||
|
||||
Assert.Contains(@"rz-selectbar", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SelectBar_Renders_Orientation()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenSelectBar<int>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Items, builder =>
|
||||
{
|
||||
builder.OpenComponent<RadzenSelectBarItem>(0);
|
||||
builder.AddAttribute(1, "Text", "Option 1");
|
||||
builder.AddAttribute(2, "Value", 1);
|
||||
builder.CloseComponent();
|
||||
});
|
||||
});
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Orientation, Orientation.Horizontal));
|
||||
Assert.Contains("rz-selectbar-horizontal", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Orientation, Orientation.Vertical));
|
||||
Assert.Contains("rz-selectbar-vertical", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SelectBar_Renders_Multiple()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
// When Multiple is true, TValue should be IEnumerable<T>
|
||||
var component = ctx.RenderComponent<RadzenSelectBar<System.Collections.Generic.IEnumerable<int>>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Multiple, true);
|
||||
parameters.Add(p => p.Items, builder =>
|
||||
{
|
||||
builder.OpenComponent<RadzenSelectBarItem>(0);
|
||||
builder.AddAttribute(1, "Text", "Option 1");
|
||||
builder.AddAttribute(2, "Value", 1);
|
||||
builder.CloseComponent();
|
||||
});
|
||||
});
|
||||
|
||||
Assert.NotNull(component.Instance);
|
||||
Assert.True(component.Instance.Multiple);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SelectBar_Renders_Size()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenSelectBar<int>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Items, builder =>
|
||||
{
|
||||
builder.OpenComponent<RadzenSelectBarItem>(0);
|
||||
builder.AddAttribute(1, "Text", "Option 1");
|
||||
builder.AddAttribute(2, "Value", 1);
|
||||
builder.CloseComponent();
|
||||
});
|
||||
});
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Size, ButtonSize.Small));
|
||||
Assert.Contains("rz-button-sm", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Size, ButtonSize.Large));
|
||||
Assert.Contains("rz-button-lg", component.Markup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
65
Radzen.Blazor.Tests/SidebarToggleTests.cs
Normal file
65
Radzen.Blazor.Tests/SidebarToggleTests.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
using Bunit;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class SidebarToggleTests
|
||||
{
|
||||
[Fact]
|
||||
public void SidebarToggle_Renders_WithClassName()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenSidebarToggle>();
|
||||
|
||||
Assert.Contains(@"rz-sidebar-toggle", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SidebarToggle_Renders_DefaultIcon()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenSidebarToggle>();
|
||||
|
||||
Assert.Contains("menu", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SidebarToggle_Renders_CustomIcon()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenSidebarToggle>();
|
||||
|
||||
var icon = "close";
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Icon, icon));
|
||||
|
||||
Assert.Contains(icon, component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SidebarToggle_Renders_AriaLabel()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenSidebarToggle>();
|
||||
|
||||
var ariaLabel = "Toggle Navigation";
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.ToggleAriaLabel, ariaLabel));
|
||||
|
||||
Assert.Contains($"aria-label=\"{ariaLabel}\"", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SidebarToggle_Raises_ClickEvent()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenSidebarToggle>();
|
||||
|
||||
var clicked = false;
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Click, args => { clicked = true; }));
|
||||
|
||||
component.Find("button").Click();
|
||||
|
||||
Assert.True(clicked);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,5 +89,71 @@ namespace Radzen.Blazor.Tests
|
||||
|
||||
Assert.Contains(@$"autofocus", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Slider_Renders_Orientation_Vertical()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenSlider<int>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Orientation, Orientation.Vertical);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-slider-vertical", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Slider_Renders_Disabled()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenSlider<int>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Disabled, true);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-state-disabled", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Slider_Renders_SliderHandle()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenSlider<int>>();
|
||||
|
||||
Assert.Contains("rz-slider-handle", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Slider_Renders_SliderRange()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenSlider<int>>();
|
||||
|
||||
Assert.Contains("rz-slider-range", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Slider_Renders_TabIndex()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenSlider<int>>();
|
||||
|
||||
Assert.Contains("tabindex=\"0\"", component.Markup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
42
Radzen.Blazor.Tests/SplitterTests.cs
Normal file
42
Radzen.Blazor.Tests/SplitterTests.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using Bunit;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class SplitterTests
|
||||
{
|
||||
[Fact]
|
||||
public void Splitter_Renders_WithClassName()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenSplitter>();
|
||||
|
||||
Assert.Contains(@"rz-splitter", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Splitter_Renders_Orientation_Horizontal()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenSplitter>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Orientation, Orientation.Horizontal);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-splitter-horizontal", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Splitter_Renders_Orientation_Vertical()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenSplitter>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Orientation, Orientation.Vertical);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-splitter-vertical", component.Markup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
98
Radzen.Blazor.Tests/StackTests.cs
Normal file
98
Radzen.Blazor.Tests/StackTests.cs
Normal file
@@ -0,0 +1,98 @@
|
||||
using Bunit;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class StackTests
|
||||
{
|
||||
[Fact]
|
||||
public void Stack_Renders_WithClassName()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenStack>();
|
||||
|
||||
Assert.Contains(@"rz-stack", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Stack_Renders_ChildContent()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenStack>(parameters =>
|
||||
{
|
||||
parameters.AddChildContent("<div>Stack Content</div>");
|
||||
});
|
||||
|
||||
Assert.Contains("Stack Content", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Stack_Renders_Orientation()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenStack>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Orientation, Orientation.Horizontal));
|
||||
Assert.Contains("rz-flex-row", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Orientation, Orientation.Vertical));
|
||||
Assert.Contains("rz-flex-column", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Stack_Renders_Gap()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenStack>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Gap, "1.5rem"));
|
||||
|
||||
Assert.Contains("--rz-gap:1.5rem", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Stack_Renders_AlignItems()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenStack>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.AlignItems, AlignItems.Center));
|
||||
|
||||
Assert.Contains("rz-align-items-center", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Stack_Renders_JustifyContent()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenStack>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.JustifyContent, JustifyContent.SpaceBetween));
|
||||
|
||||
Assert.Contains("rz-justify-content-space-between", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Stack_Renders_Wrap()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenStack>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Wrap, FlexWrap.Wrap));
|
||||
|
||||
Assert.Contains("flex-wrap:wrap", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Stack_Renders_Reverse()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenStack>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Reverse, true));
|
||||
|
||||
Assert.Contains("rz-flex-column-reverse", component.Markup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
70
Radzen.Blazor.Tests/StepsTests.cs
Normal file
70
Radzen.Blazor.Tests/StepsTests.cs
Normal file
@@ -0,0 +1,70 @@
|
||||
using Bunit;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class StepsTests
|
||||
{
|
||||
[Fact]
|
||||
public void Steps_Renders_WithClassName()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenSteps>();
|
||||
|
||||
Assert.Contains(@"rz-steps", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Steps_Renders_ShowStepsButtons_True()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenSteps>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.ShowStepsButtons, true);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-steps-buttons", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Steps_Renders_ShowStepsButtons_False()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenSteps>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.ShowStepsButtons, false);
|
||||
});
|
||||
|
||||
Assert.DoesNotContain("rz-steps-buttons", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Steps_Renders_StepsButtons()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenSteps>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.ShowStepsButtons, true);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-steps-prev", component.Markup);
|
||||
Assert.Contains("rz-steps-next", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Steps_Renders_CustomButtonText()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenSteps>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.ShowStepsButtons, true);
|
||||
parameters.Add(p => p.NextText, "Continue");
|
||||
parameters.Add(p => p.PreviousText, "Back");
|
||||
});
|
||||
|
||||
Assert.Contains("Continue", component.Markup);
|
||||
Assert.Contains("Back", component.Markup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
100
Radzen.Blazor.Tests/TableTests.cs
Normal file
100
Radzen.Blazor.Tests/TableTests.cs
Normal file
@@ -0,0 +1,100 @@
|
||||
using Bunit;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class TableTests
|
||||
{
|
||||
[Fact]
|
||||
public void Table_Renders_WithClassName()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenTable>();
|
||||
|
||||
Assert.Contains(@"rz-datatable", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Table_Renders_TableElement()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenTable>();
|
||||
|
||||
Assert.Contains("rz-grid-table", component.Markup);
|
||||
Assert.Contains("<table", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Table_Renders_GridLines_None()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenTable>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.GridLines, DataGridGridLines.None);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-grid-gridlines-none", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Table_Renders_GridLines_Horizontal()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenTable>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.GridLines, DataGridGridLines.Horizontal);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-grid-gridlines-horizontal", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Table_Renders_GridLines_Vertical()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenTable>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.GridLines, DataGridGridLines.Vertical);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-grid-gridlines-vertical", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Table_Renders_GridLines_Both()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenTable>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.GridLines, DataGridGridLines.Both);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-grid-gridlines-both", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Table_Renders_AllowAlternatingRows_True()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenTable>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowAlternatingRows, true);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-grid-table-striped", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Table_Renders_AllowAlternatingRows_False()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenTable>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowAlternatingRows, false);
|
||||
});
|
||||
|
||||
Assert.DoesNotContain("rz-grid-table-striped", component.Markup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
108
Radzen.Blazor.Tests/TabsTests.cs
Normal file
108
Radzen.Blazor.Tests/TabsTests.cs
Normal file
@@ -0,0 +1,108 @@
|
||||
using Bunit;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class TabsTests
|
||||
{
|
||||
[Fact]
|
||||
public void Tabs_Renders_WithClassName()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenTabs>();
|
||||
|
||||
Assert.Contains(@"rz-tabview", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Tabs_Renders_TabPosition_Top()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenTabs>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.TabPosition, TabPosition.Top);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-tabview-top", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Tabs_Renders_TabPosition_Bottom()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenTabs>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.TabPosition, TabPosition.Bottom);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-tabview-bottom", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Tabs_Renders_TabPosition_Left()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenTabs>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.TabPosition, TabPosition.Left);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-tabview-left", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Tabs_Renders_TabPosition_Right()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenTabs>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.TabPosition, TabPosition.Right);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-tabview-right", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Tabs_Renders_TabPosition_TopRight()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenTabs>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.TabPosition, TabPosition.TopRight);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-tabview-top-right", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Tabs_Renders_TabPosition_BottomRight()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenTabs>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.TabPosition, TabPosition.BottomRight);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-tabview-bottom-right", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Tabs_Renders_TabNav()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenTabs>();
|
||||
|
||||
Assert.Contains("rz-tabview-nav", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Tabs_Renders_TabPanels()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenTabs>();
|
||||
|
||||
Assert.Contains("rz-tabview-panels", component.Markup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
71
Radzen.Blazor.Tests/TimelineTests.cs
Normal file
71
Radzen.Blazor.Tests/TimelineTests.cs
Normal file
@@ -0,0 +1,71 @@
|
||||
using Bunit;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class TimelineTests
|
||||
{
|
||||
[Fact]
|
||||
public void Timeline_Renders_WithClassName()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenTimeline>();
|
||||
|
||||
Assert.Contains(@"rz-timeline", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Timeline_Renders_Orientation()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenTimeline>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Orientation, Orientation.Horizontal));
|
||||
Assert.Contains("rz-timeline-row", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Orientation, Orientation.Vertical));
|
||||
Assert.Contains("rz-timeline-column", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Timeline_Renders_LinePosition()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenTimeline>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.LinePosition, LinePosition.Start));
|
||||
Assert.Contains("rz-timeline-start", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.LinePosition, LinePosition.End));
|
||||
Assert.Contains("rz-timeline-end", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.LinePosition, LinePosition.Center));
|
||||
Assert.Contains("rz-timeline-center", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Timeline_Renders_AlignItems()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenTimeline>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.AlignItems, AlignItems.Start));
|
||||
Assert.Contains("rz-timeline-align-items-start", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.AlignItems, AlignItems.Center));
|
||||
Assert.Contains("rz-timeline-align-items-center", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Timeline_Renders_Reverse()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenTimeline>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Reverse, true));
|
||||
|
||||
Assert.Contains("rz-timeline-reverse", component.Markup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
27
Radzen.Blazor.Tests/TocTests.cs
Normal file
27
Radzen.Blazor.Tests/TocTests.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using Bunit;
|
||||
using System.Collections.Generic;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class TocTests
|
||||
{
|
||||
[Fact]
|
||||
public void TocItem_Renders_With_Attributes()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenTocItem>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Attributes, new Dictionary<string, object>
|
||||
{
|
||||
{ "data-enhance-nav", "false" },
|
||||
{ "aria-label", "Table of Contents Item" }
|
||||
});
|
||||
});
|
||||
|
||||
Assert.Contains("data-enhance-nav=\"false\"", component.Markup);
|
||||
Assert.Contains("aria-label=\"Table of Contents Item\"", component.Markup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
98
Radzen.Blazor.Tests/ToggleButtonTests.cs
Normal file
98
Radzen.Blazor.Tests/ToggleButtonTests.cs
Normal file
@@ -0,0 +1,98 @@
|
||||
using Bunit;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class ToggleButtonTests
|
||||
{
|
||||
[Fact]
|
||||
public void ToggleButton_Renders_WithClassName()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenToggleButton>();
|
||||
|
||||
Assert.Contains(@"rz-button", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ToggleButton_Renders_TextParameter()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenToggleButton>();
|
||||
|
||||
var text = "Toggle Me";
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Text, text));
|
||||
|
||||
Assert.Contains(text, component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ToggleButton_Renders_IconParameter()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenToggleButton>();
|
||||
|
||||
var icon = "toggle_on";
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Icon, icon));
|
||||
|
||||
Assert.Contains(icon, component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ToggleButton_Renders_Value()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenToggleButton>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Value, true));
|
||||
|
||||
Assert.Contains("rz-state-active", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ToggleButton_Renders_Disabled()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenToggleButton>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Disabled, true));
|
||||
|
||||
Assert.Contains("rz-state-disabled", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ToggleButton_Renders_ButtonStyle()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenToggleButton>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.ButtonStyle, ButtonStyle.Primary));
|
||||
Assert.Contains("rz-primary", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.ButtonStyle, ButtonStyle.Success));
|
||||
Assert.Contains("rz-success", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ToggleButton_Raises_ChangeEvent()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenToggleButton>();
|
||||
|
||||
var changed = false;
|
||||
bool newValue = false;
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Change, args =>
|
||||
{
|
||||
changed = true;
|
||||
newValue = args;
|
||||
}));
|
||||
|
||||
component.Find("button").Click();
|
||||
|
||||
Assert.True(changed);
|
||||
Assert.True(newValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
211
Radzen.Blazor.Tests/TreeTests.cs
Normal file
211
Radzen.Blazor.Tests/TreeTests.cs
Normal file
@@ -0,0 +1,211 @@
|
||||
using Bunit;
|
||||
using Xunit;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class TreeTests
|
||||
{
|
||||
class Category
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public List<Product> Products { get; set; } = new List<Product>();
|
||||
}
|
||||
|
||||
class Product
|
||||
{
|
||||
public string Name { get; set; }
|
||||
}
|
||||
|
||||
class Employee
|
||||
{
|
||||
public string FirstName { get; set; }
|
||||
public string LastName { get; set; }
|
||||
public List<Employee> Employees { get; set; } = new List<Employee>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Tree_Renders_WithClassName()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenTree>();
|
||||
|
||||
Assert.Contains(@"rz-tree", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Tree_Renders_TreeContainer()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenTree>();
|
||||
|
||||
Assert.Contains("rz-tree-container", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Tree_Renders_TabIndex()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenTree>();
|
||||
|
||||
Assert.Contains("tabindex=\"0\"", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Tree_Renders_WithData_SingleLevel()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var data = new List<Category>
|
||||
{
|
||||
new Category { Name = "Electronics" },
|
||||
new Category { Name = "Clothing" }
|
||||
};
|
||||
|
||||
var component = ctx.RenderComponent<RadzenTree>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Data, data);
|
||||
parameters.Add(p => p.ChildContent, builder =>
|
||||
{
|
||||
builder.OpenComponent<RadzenTreeLevel>(0);
|
||||
builder.AddAttribute(1, "TextProperty", "Name");
|
||||
builder.CloseComponent();
|
||||
});
|
||||
});
|
||||
|
||||
Assert.Contains("Electronics", component.Markup);
|
||||
Assert.Contains("Clothing", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Tree_Renders_WithData_HierarchicalData()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var data = new List<Category>
|
||||
{
|
||||
new Category
|
||||
{
|
||||
Name = "Electronics",
|
||||
Products = new List<Product>
|
||||
{
|
||||
new Product { Name = "Laptop" },
|
||||
new Product { Name = "Phone" }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var component = ctx.RenderComponent<RadzenTree>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Data, data);
|
||||
parameters.Add(p => p.ChildContent, builder =>
|
||||
{
|
||||
builder.OpenComponent<RadzenTreeLevel>(0);
|
||||
builder.AddAttribute(1, "TextProperty", "Name");
|
||||
builder.AddAttribute(2, "ChildrenProperty", "Products");
|
||||
builder.CloseComponent();
|
||||
|
||||
builder.OpenComponent<RadzenTreeLevel>(3);
|
||||
builder.AddAttribute(4, "TextProperty", "Name");
|
||||
builder.AddAttribute(5, "HasChildren", (object product) => false);
|
||||
builder.CloseComponent();
|
||||
});
|
||||
});
|
||||
|
||||
Assert.Contains("Electronics", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Tree_Renders_WithData_SelfReferencing()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var data = new List<Employee>
|
||||
{
|
||||
new Employee
|
||||
{
|
||||
FirstName = "Nancy",
|
||||
LastName = "Davolio",
|
||||
Employees = new List<Employee>
|
||||
{
|
||||
new Employee { FirstName = "Andrew", LastName = "Fuller" }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var component = ctx.RenderComponent<RadzenTree>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Data, data);
|
||||
parameters.Add(p => p.ChildContent, builder =>
|
||||
{
|
||||
builder.OpenComponent<RadzenTreeLevel>(0);
|
||||
builder.AddAttribute(1, "TextProperty", "LastName");
|
||||
builder.AddAttribute(2, "ChildrenProperty", "Employees");
|
||||
builder.AddAttribute(3, "HasChildren", (object e) => (e as Employee).Employees.Any());
|
||||
builder.CloseComponent();
|
||||
});
|
||||
});
|
||||
|
||||
Assert.Contains("Davolio", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Tree_Renders_WithCheckBoxes()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var data = new List<Category>
|
||||
{
|
||||
new Category { Name = "Electronics" }
|
||||
};
|
||||
|
||||
var component = ctx.RenderComponent<RadzenTree>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowCheckBoxes, true);
|
||||
parameters.Add(p => p.Data, data);
|
||||
parameters.Add(p => p.ChildContent, builder =>
|
||||
{
|
||||
builder.OpenComponent<RadzenTreeLevel>(0);
|
||||
builder.AddAttribute(1, "TextProperty", "Name");
|
||||
builder.CloseComponent();
|
||||
});
|
||||
});
|
||||
|
||||
Assert.Contains("rz-chkbox", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Tree_Renders_WithExpandableItems()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var data = new List<Category>
|
||||
{
|
||||
new Category
|
||||
{
|
||||
Name = "Electronics",
|
||||
Products = new List<Product>
|
||||
{
|
||||
new Product { Name = "Laptop" }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var component = ctx.RenderComponent<RadzenTree>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Data, data);
|
||||
parameters.Add(p => p.ChildContent, builder =>
|
||||
{
|
||||
builder.OpenComponent<RadzenTreeLevel>(0);
|
||||
builder.AddAttribute(1, "TextProperty", "Name");
|
||||
builder.AddAttribute(2, "ChildrenProperty", "Products");
|
||||
builder.CloseComponent();
|
||||
|
||||
builder.OpenComponent<RadzenTreeLevel>(3);
|
||||
builder.AddAttribute(4, "TextProperty", "Name");
|
||||
builder.CloseComponent();
|
||||
});
|
||||
});
|
||||
|
||||
// Expandable items should have a toggle icon
|
||||
Assert.Contains("rz-tree-toggler", component.Markup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
95
Radzen.Blazor.Tests/UploadTests.cs
Normal file
95
Radzen.Blazor.Tests/UploadTests.cs
Normal file
@@ -0,0 +1,95 @@
|
||||
using Bunit;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class UploadTests
|
||||
{
|
||||
[Fact]
|
||||
public void Upload_Renders_WithClassName()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenUpload>();
|
||||
|
||||
Assert.Contains(@"rz-fileupload", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Upload_Renders_Disabled()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenUpload>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Disabled, true);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-state-disabled", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Upload_Renders_ChooseText()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenUpload>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.ChooseText, "Select Files");
|
||||
});
|
||||
|
||||
Assert.Contains("Select Files", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Upload_Renders_DefaultChooseText()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenUpload>();
|
||||
|
||||
Assert.Contains("Choose", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Upload_Renders_Icon()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenUpload>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Icon, "upload");
|
||||
});
|
||||
|
||||
Assert.Contains("upload", component.Markup);
|
||||
Assert.Contains("rzi", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Upload_Renders_Multiple_Attribute()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenUpload>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Multiple, true);
|
||||
});
|
||||
|
||||
Assert.Contains("multiple", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Upload_Renders_Accept_Attribute()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenUpload>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Accept, "image/*");
|
||||
});
|
||||
|
||||
Assert.Contains("accept=\"image/*\"", component.Markup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,48 @@ root = true
|
||||
|
||||
#### Core EditorConfig Options ####
|
||||
|
||||
dotnet_diagnostic.CA1002.severity = none
|
||||
dotnet_diagnostic.CA1003.severity = none
|
||||
dotnet_diagnostic.CA1024.severity = none
|
||||
dotnet_diagnostic.CA1030.severity = none
|
||||
dotnet_diagnostic.CA1031.severity = none
|
||||
dotnet_diagnostic.CA1033.severity = none
|
||||
dotnet_diagnostic.CA1044.severity = none
|
||||
dotnet_diagnostic.CA1050.severity = none
|
||||
dotnet_diagnostic.CA1051.severity = none
|
||||
dotnet_diagnostic.CA1052.severity = none
|
||||
dotnet_diagnostic.CA1054.severity = none
|
||||
dotnet_diagnostic.CA1055.severity = none
|
||||
dotnet_diagnostic.CA1056.severity = none
|
||||
dotnet_diagnostic.CA1063.severity = none
|
||||
dotnet_diagnostic.CA1068.severity = none
|
||||
dotnet_diagnostic.CA1308.severity = none
|
||||
dotnet_diagnostic.CA1708.severity = none
|
||||
dotnet_diagnostic.CA1711.severity = none
|
||||
dotnet_diagnostic.CA1716.severity = none
|
||||
dotnet_diagnostic.CA1720.severity = none
|
||||
dotnet_diagnostic.CA1721.severity = none
|
||||
dotnet_diagnostic.CA1724.severity = none
|
||||
dotnet_diagnostic.CA1725.severity = none
|
||||
dotnet_diagnostic.CA1802.severity = none
|
||||
dotnet_diagnostic.CA1814.severity = none
|
||||
dotnet_diagnostic.CA1815.severity = none
|
||||
dotnet_diagnostic.CA1816.severity = none
|
||||
dotnet_diagnostic.CA1819.severity = none
|
||||
dotnet_diagnostic.CA1822.severity = none
|
||||
dotnet_diagnostic.CA1827.severity = none
|
||||
dotnet_diagnostic.CA1834.severity = none
|
||||
dotnet_diagnostic.CA1845.severity = none
|
||||
dotnet_diagnostic.CA1849.severity = none
|
||||
dotnet_diagnostic.CA1851.severity = none
|
||||
dotnet_diagnostic.CA1859.severity = none
|
||||
dotnet_diagnostic.CA1863.severity = none
|
||||
dotnet_diagnostic.CA1869.severity = none
|
||||
dotnet_diagnostic.CA2007.severity = none
|
||||
dotnet_diagnostic.CA2012.severity = none
|
||||
dotnet_diagnostic.CA2211.severity = none
|
||||
dotnet_diagnostic.CA2227.severity = none
|
||||
|
||||
# Indentation and spacing
|
||||
indent_size = 4
|
||||
indent_style = space
|
||||
@@ -13,218 +13,16 @@ using System.Linq;
|
||||
|
||||
namespace Radzen;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a chat message in the conversation history.
|
||||
/// </summary>
|
||||
public class ChatMessage
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the role of the message sender (system, user, or assistant).
|
||||
/// </summary>
|
||||
public string Role { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the content of the message.
|
||||
/// </summary>
|
||||
public string Content { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the timestamp when the message was created.
|
||||
/// </summary>
|
||||
public DateTime Timestamp { get; set; } = DateTime.Now;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a conversation session with memory.
|
||||
/// </summary>
|
||||
public class ConversationSession
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the unique identifier for the conversation session.
|
||||
/// </summary>
|
||||
public string Id { get; set; } = Guid.NewGuid().ToString();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the list of messages in the conversation.
|
||||
/// </summary>
|
||||
public List<ChatMessage> Messages { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the timestamp when the conversation was created.
|
||||
/// </summary>
|
||||
public DateTime CreatedAt { get; set; } = DateTime.Now;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the timestamp when the conversation was last updated.
|
||||
/// </summary>
|
||||
public DateTime LastUpdated { get; set; } = DateTime.Now;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum number of messages to keep in memory.
|
||||
/// </summary>
|
||||
public int MaxMessages { get; set; } = 50;
|
||||
|
||||
/// <summary>
|
||||
/// Adds a message to the conversation and manages memory limits.
|
||||
/// </summary>
|
||||
/// <param name="role">The role of the message sender.</param>
|
||||
/// <param name="content">The message content.</param>
|
||||
public void AddMessage(string role, string content)
|
||||
{
|
||||
Messages.Add(new ChatMessage
|
||||
{
|
||||
Role = role,
|
||||
Content = content,
|
||||
Timestamp = DateTime.Now
|
||||
});
|
||||
|
||||
LastUpdated = DateTime.Now;
|
||||
|
||||
// Remove oldest messages if we exceed the limit
|
||||
while (Messages.Count > MaxMessages)
|
||||
{
|
||||
Messages.RemoveAt(0);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all messages from the conversation.
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
Messages.Clear();
|
||||
LastUpdated = DateTime.Now;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the conversation messages formatted for the AI API.
|
||||
/// </summary>
|
||||
/// <param name="systemPrompt">The system prompt to include.</param>
|
||||
/// <returns>A list of message objects for the AI API.</returns>
|
||||
public List<object> GetFormattedMessages(string systemPrompt)
|
||||
{
|
||||
var messages = new List<object>();
|
||||
|
||||
// Add system message
|
||||
messages.Add(new { role = "system", content = systemPrompt });
|
||||
|
||||
// Add conversation messages
|
||||
foreach (var message in Messages)
|
||||
{
|
||||
messages.Add(new { role = message.Role, content = message.Content });
|
||||
}
|
||||
|
||||
return messages;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interface for getting chat completions from an AI model with conversation memory.
|
||||
/// </summary>
|
||||
public interface IAIChatService
|
||||
{
|
||||
/// <summary>
|
||||
/// Streams chat completion responses from the AI model asynchronously with conversation memory.
|
||||
/// </summary>
|
||||
/// <param name="userInput">The user's input message to send to the AI model.</param>
|
||||
/// <param name="sessionId">Optional session ID to maintain conversation context. If null, a new session will be created.</param>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used to cancel the operation.</param>
|
||||
/// <param name="model">Optional model name to override the configured model.</param>
|
||||
/// <param name="systemPrompt">Optional system prompt to override the configured system prompt.</param>
|
||||
/// <param name="temperature">Optional temperature to override the configured temperature.</param>
|
||||
/// <param name="maxTokens">Optional maximum tokens to override the configured max tokens.</param>
|
||||
/// <returns>An async enumerable that yields streaming response chunks from the AI model.</returns>
|
||||
IAsyncEnumerable<string> GetCompletionsAsync(string userInput, string sessionId = null, CancellationToken cancellationToken = default, string model = null, string systemPrompt = null, double? temperature = null, int? maxTokens = null);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or creates a conversation session.
|
||||
/// </summary>
|
||||
/// <param name="sessionId">The session ID. If null, a new session will be created.</param>
|
||||
/// <returns>The conversation session.</returns>
|
||||
ConversationSession GetOrCreateSession(string sessionId = null);
|
||||
|
||||
/// <summary>
|
||||
/// Clears the conversation history for a specific session.
|
||||
/// </summary>
|
||||
/// <param name="sessionId">The session ID to clear.</param>
|
||||
void ClearSession(string sessionId);
|
||||
|
||||
/// <summary>
|
||||
/// Gets all active conversation sessions.
|
||||
/// </summary>
|
||||
/// <returns>A list of active conversation sessions.</returns>
|
||||
IEnumerable<ConversationSession> GetActiveSessions();
|
||||
|
||||
/// <summary>
|
||||
/// Removes old conversation sessions based on age.
|
||||
/// </summary>
|
||||
/// <param name="maxAgeHours">Maximum age in hours for sessions to keep.</param>
|
||||
void CleanupOldSessions(int maxAgeHours = 24);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configuration options for the <see cref="AIChatService"/>.
|
||||
/// </summary>
|
||||
public class AIChatServiceOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the endpoint URL for the AI service.
|
||||
/// </summary>
|
||||
public string Endpoint { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the proxy URL for the AI service, if any. If set, this will override the Endpoint.
|
||||
/// </summary>
|
||||
public string Proxy { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the API key for authentication with the AI service.
|
||||
/// </summary>
|
||||
public string ApiKey { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the header name for the API key (e.g., 'Authorization' or 'api-key').
|
||||
/// </summary>
|
||||
public string ApiKeyHeader { get; set; } = "Authorization";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the model name to use for executing chat completions (e.g., 'gpt-3.5-turbo').
|
||||
/// </summary>
|
||||
public string Model { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the system prompt for the AI assistant.
|
||||
/// </summary>
|
||||
public string SystemPrompt { get; set; } = "You are a helpful AI code assistant.";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the temperature for the AI model (0.0 to 2.0). Set to 0.0 for deterministic responses, higher values for more creative outputs.
|
||||
/// </summary>
|
||||
public double Temperature { get; set; } = 0.7;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum number of tokens to generate in the response.
|
||||
/// </summary>
|
||||
public int? MaxTokens { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum number of messages to keep in conversation memory.
|
||||
/// </summary>
|
||||
public int MaxMessages { get; set; } = 50;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum age in hours for conversation sessions before cleanup.
|
||||
/// </summary>
|
||||
public int SessionMaxAgeHours { get; set; } = 24;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Service for interacting with AI chat models to get completions with conversation memory.
|
||||
/// </summary>
|
||||
public class AIChatService(IServiceProvider serviceProvider, IOptions<AIChatServiceOptions> options) : IAIChatService
|
||||
{
|
||||
private readonly Dictionary<string, ConversationSession> _sessions = new();
|
||||
private readonly object _sessionsLock = new();
|
||||
private readonly Dictionary<string, ConversationSession> sessions = new();
|
||||
private readonly object sessionsLock = new();
|
||||
|
||||
// Add this static field to cache the JsonSerializerOptions instance
|
||||
private static readonly JsonSerializerOptions CachedJsonSerializerOptions = new JsonSerializerOptions { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull };
|
||||
|
||||
/// <summary>
|
||||
/// Gets the configuration options for the chat streaming service.
|
||||
@@ -232,7 +30,7 @@ public class AIChatService(IServiceProvider serviceProvider, IOptions<AIChatServ
|
||||
public AIChatServiceOptions Options => options.Value;
|
||||
|
||||
/// <inheritdoc />
|
||||
public async IAsyncEnumerable<string> GetCompletionsAsync(string userInput, string sessionId = null, [System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken cancellationToken = default, string model = null, string systemPrompt = null, double? temperature = null, int? maxTokens = null)
|
||||
public async IAsyncEnumerable<string> GetCompletionsAsync(string userInput, string? sessionId = null, [System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken cancellationToken = default, string? model = null, string? systemPrompt = null, double? temperature = null, int? maxTokens = null, string? endpoint = null, string? proxy = null, string? apiKey = null, string? apiKeyHeader = null)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(userInput))
|
||||
{
|
||||
@@ -241,11 +39,14 @@ public class AIChatService(IServiceProvider serviceProvider, IOptions<AIChatServ
|
||||
|
||||
// Get or create session
|
||||
var session = GetOrCreateSession(sessionId);
|
||||
|
||||
|
||||
// Add user message to conversation history
|
||||
session.AddMessage("user", userInput);
|
||||
|
||||
var url = Options.Proxy ?? Options.Endpoint;
|
||||
// Use runtime parameters or fall back to configured options
|
||||
var url = proxy ?? Options.Proxy ?? endpoint ?? Options.Endpoint;
|
||||
var effectiveApiKey = apiKey ?? Options.ApiKey;
|
||||
var effectiveApiKeyHeader = apiKeyHeader ?? Options.ApiKeyHeader;
|
||||
|
||||
// Get formatted messages including conversation history
|
||||
var messages = session.GetFormattedMessages(systemPrompt ?? Options.SystemPrompt);
|
||||
@@ -259,41 +60,45 @@ public class AIChatService(IServiceProvider serviceProvider, IOptions<AIChatServ
|
||||
stream = true
|
||||
};
|
||||
|
||||
var request = new HttpRequestMessage(HttpMethod.Post, url)
|
||||
using var request = new HttpRequestMessage(HttpMethod.Post, url)
|
||||
{
|
||||
Content = new StringContent(JsonSerializer.Serialize(payload, new JsonSerializerOptions { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull }), Encoding.UTF8, "application/json")
|
||||
Content = new StringContent(JsonSerializer.Serialize(payload, CachedJsonSerializerOptions), Encoding.UTF8, "application/json")
|
||||
};
|
||||
|
||||
if (!string.IsNullOrEmpty(Options.ApiKey))
|
||||
if (!string.IsNullOrEmpty(effectiveApiKey))
|
||||
{
|
||||
if (string.Equals(Options.ApiKeyHeader, "Authorization", StringComparison.OrdinalIgnoreCase))
|
||||
if (string.IsNullOrWhiteSpace(effectiveApiKeyHeader))
|
||||
{
|
||||
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", Options.ApiKey);
|
||||
throw new InvalidOperationException("API key header must be specified when an API key is provided.");
|
||||
}
|
||||
|
||||
if (string.Equals(effectiveApiKeyHeader, "Authorization", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", effectiveApiKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
request.Headers.Add(Options.ApiKeyHeader, Options.ApiKey);
|
||||
request.Headers.Add(effectiveApiKeyHeader, effectiveApiKey);
|
||||
}
|
||||
}
|
||||
|
||||
var httpClient = serviceProvider.GetRequiredService<HttpClient>();
|
||||
var response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
|
||||
var response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
throw new Exception($"Chat stream failed: {await response.Content.ReadAsStringAsync(cancellationToken)}");
|
||||
throw new HttpRequestException($"Chat stream failed: {await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false)}");
|
||||
}
|
||||
|
||||
using var stream = await response.Content.ReadAsStreamAsync(cancellationToken);
|
||||
using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
|
||||
using var reader = new StreamReader(stream);
|
||||
|
||||
var assistantResponse = new StringBuilder();
|
||||
|
||||
while (!reader.EndOfStream && !cancellationToken.IsCancellationRequested)
|
||||
string? line;
|
||||
while ((line = await reader.ReadLineAsync()) is not null && !cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
var line = await reader.ReadLineAsync();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(line) || !line.StartsWith("data:"))
|
||||
if (string.IsNullOrWhiteSpace(line) || !line.StartsWith("data:", StringComparison.Ordinal))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -321,23 +126,23 @@ public class AIChatService(IServiceProvider serviceProvider, IOptions<AIChatServ
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public ConversationSession GetOrCreateSession(string sessionId = null)
|
||||
public ConversationSession GetOrCreateSession(string? sessionId = null)
|
||||
{
|
||||
lock (_sessionsLock)
|
||||
lock (sessionsLock)
|
||||
{
|
||||
if (string.IsNullOrEmpty(sessionId))
|
||||
{
|
||||
sessionId = Guid.NewGuid().ToString();
|
||||
}
|
||||
|
||||
if (!_sessions.TryGetValue(sessionId, out var session))
|
||||
if (!sessions.TryGetValue(sessionId, out var session))
|
||||
{
|
||||
session = new ConversationSession
|
||||
{
|
||||
Id = sessionId,
|
||||
MaxMessages = Options.MaxMessages
|
||||
};
|
||||
_sessions[sessionId] = session;
|
||||
sessions[sessionId] = session;
|
||||
}
|
||||
|
||||
return session;
|
||||
@@ -347,9 +152,9 @@ public class AIChatService(IServiceProvider serviceProvider, IOptions<AIChatServ
|
||||
/// <inheritdoc />
|
||||
public void ClearSession(string sessionId)
|
||||
{
|
||||
lock (_sessionsLock)
|
||||
lock (sessionsLock)
|
||||
{
|
||||
if (_sessions.TryGetValue(sessionId, out var session))
|
||||
if (sessions.TryGetValue(sessionId, out var session))
|
||||
{
|
||||
session.Clear();
|
||||
}
|
||||
@@ -359,35 +164,40 @@ public class AIChatService(IServiceProvider serviceProvider, IOptions<AIChatServ
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<ConversationSession> GetActiveSessions()
|
||||
{
|
||||
lock (_sessionsLock)
|
||||
lock (sessionsLock)
|
||||
{
|
||||
return _sessions.Values.ToList();
|
||||
return sessions.Values.ToList();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void CleanupOldSessions(int maxAgeHours = 24)
|
||||
{
|
||||
lock (_sessionsLock)
|
||||
lock (sessionsLock)
|
||||
{
|
||||
var cutoffTime = DateTime.Now.AddHours(-maxAgeHours);
|
||||
var sessionsToRemove = _sessions.Values
|
||||
var sessionsToRemove = sessions.Values
|
||||
.Where(s => s.LastUpdated < cutoffTime)
|
||||
.Select(s => s.Id)
|
||||
.ToList();
|
||||
|
||||
foreach (var sessionId in sessionsToRemove)
|
||||
{
|
||||
_sessions.Remove(sessionId);
|
||||
sessions.Remove(sessionId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static string ParseStreamingResponse(string json)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(json))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var doc = JsonDocument.Parse(json);
|
||||
using var doc = JsonDocument.Parse(json);
|
||||
var root = doc.RootElement;
|
||||
|
||||
if (!root.TryGetProperty("choices", out var choices) || choices.GetArrayLength() == 0)
|
||||
@@ -409,52 +219,13 @@ public class AIChatService(IServiceProvider serviceProvider, IOptions<AIChatServ
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
catch
|
||||
catch (JsonException)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
catch (FormatException)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extension methods for configuring AIChatService in the dependency injection container.
|
||||
/// </summary>
|
||||
public static class AIChatServiceExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds the AIChatService to the service collection with the specified configuration.
|
||||
/// </summary>
|
||||
/// <param name="services">The service collection.</param>
|
||||
/// <param name="configureOptions">The action to configure the AIChatService options.</param>
|
||||
/// <returns>The updated service collection.</returns>
|
||||
public static IServiceCollection AddAIChatService(this IServiceCollection services, Action<AIChatServiceOptions> configureOptions)
|
||||
{
|
||||
if (services == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(services));
|
||||
}
|
||||
|
||||
if (configureOptions == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(configureOptions));
|
||||
}
|
||||
|
||||
services.Configure(configureOptions);
|
||||
services.AddScoped<IAIChatService, AIChatService>();
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the AIChatService to the service collection with default options.
|
||||
/// </summary>
|
||||
/// <param name="services">The service collection.</param>
|
||||
/// <returns>The updated service collection.</returns>
|
||||
public static IServiceCollection AddAIChatService(this IServiceCollection services)
|
||||
{
|
||||
services.AddOptions<AIChatServiceOptions>();
|
||||
services.AddScoped<IAIChatService, AIChatService>();
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
41
Radzen.Blazor/AIChatServiceExtensions.cs
Normal file
41
Radzen.Blazor/AIChatServiceExtensions.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
using System;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Radzen;
|
||||
|
||||
/// <summary>
|
||||
/// Extension methods for configuring AIChatService in the dependency injection container.
|
||||
/// </summary>
|
||||
public static class AIChatServiceExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds the AIChatService to the service collection with the specified configuration.
|
||||
/// </summary>
|
||||
/// <param name="services">The service collection.</param>
|
||||
/// <param name="configureOptions">The action to configure the AIChatService options.</param>
|
||||
/// <returns>The updated service collection.</returns>
|
||||
public static IServiceCollection AddAIChatService(this IServiceCollection services, Action<AIChatServiceOptions> configureOptions)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(services);
|
||||
ArgumentNullException.ThrowIfNull(configureOptions);
|
||||
|
||||
services.Configure(configureOptions);
|
||||
services.AddScoped<IAIChatService, AIChatService>();
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the AIChatService to the service collection with default options.
|
||||
/// </summary>
|
||||
/// <param name="services">The service collection.</param>
|
||||
/// <returns>The updated service collection.</returns>
|
||||
public static IServiceCollection AddAIChatService(this IServiceCollection services)
|
||||
{
|
||||
services.AddOptions<AIChatServiceOptions>();
|
||||
services.AddScoped<IAIChatService, AIChatService>();
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
|
||||
58
Radzen.Blazor/AIChatServiceOptions.cs
Normal file
58
Radzen.Blazor/AIChatServiceOptions.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
namespace Radzen;
|
||||
|
||||
/// <summary>
|
||||
/// Configuration options for the <see cref="AIChatService"/>.
|
||||
/// </summary>
|
||||
public class AIChatServiceOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the endpoint URL for the AI service.
|
||||
/// </summary>
|
||||
public string Endpoint { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the proxy URL for the AI service, if any. If set, this will override the Endpoint.
|
||||
/// </summary>
|
||||
public string? Proxy { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the API key for authentication with the AI service.
|
||||
/// </summary>
|
||||
public string ApiKey { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the header name for the API key (e.g., 'Authorization' or 'api-key').
|
||||
/// </summary>
|
||||
public string ApiKeyHeader { get; set; } = "Authorization";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the model name to use for executing chat completions (e.g., 'gpt-3.5-turbo').
|
||||
/// </summary>
|
||||
public string? Model { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the system prompt for the AI assistant.
|
||||
/// </summary>
|
||||
public string SystemPrompt { get; set; } = "You are a helpful AI code assistant.";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the temperature for the AI model (0.0 to 2.0). Set to 0.0 for deterministic responses, higher values for more creative outputs.
|
||||
/// </summary>
|
||||
public double Temperature { get; set; } = 0.7;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum number of tokens to generate in the response.
|
||||
/// </summary>
|
||||
public int? MaxTokens { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum number of messages to keep in conversation memory.
|
||||
/// </summary>
|
||||
public int MaxMessages { get; set; } = 50;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum age in hours for conversation sessions before cleanup.
|
||||
/// </summary>
|
||||
public int SessionMaxAgeHours { get; set; } = 24;
|
||||
}
|
||||
|
||||
43
Radzen.Blazor/AggregateFunction.cs
Normal file
43
Radzen.Blazor/AggregateFunction.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
namespace Radzen;
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the aggregate function for pivot values.
|
||||
/// </summary>
|
||||
public enum AggregateFunction
|
||||
{
|
||||
/// <summary>
|
||||
/// Sum of values.
|
||||
/// </summary>
|
||||
Sum,
|
||||
|
||||
/// <summary>
|
||||
/// Count of items.
|
||||
/// </summary>
|
||||
Count,
|
||||
|
||||
/// <summary>
|
||||
/// Average of values.
|
||||
/// </summary>
|
||||
Average,
|
||||
|
||||
/// <summary>
|
||||
/// Minimum value.
|
||||
/// </summary>
|
||||
Min,
|
||||
|
||||
/// <summary>
|
||||
/// Maximum value.
|
||||
/// </summary>
|
||||
Max,
|
||||
|
||||
/// <summary>
|
||||
/// First value.
|
||||
/// </summary>
|
||||
First,
|
||||
|
||||
/// <summary>
|
||||
/// Last value.
|
||||
/// </summary>
|
||||
Last
|
||||
}
|
||||
|
||||
28
Radzen.Blazor/AlertSize.cs
Normal file
28
Radzen.Blazor/AlertSize.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
namespace Radzen;
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the size of a <see cref="Radzen.Blazor.RadzenAlert" />.
|
||||
/// </summary>
|
||||
public enum AlertSize
|
||||
{
|
||||
/// <summary>
|
||||
/// The smallest alert.
|
||||
/// </summary>
|
||||
ExtraSmall,
|
||||
|
||||
/// <summary>
|
||||
/// A alert smaller than the default.
|
||||
/// </summary>
|
||||
Small,
|
||||
|
||||
/// <summary>
|
||||
/// The default size of an alert.
|
||||
/// </summary>
|
||||
Medium,
|
||||
|
||||
/// <summary>
|
||||
/// An alert larger than the default.
|
||||
/// </summary>
|
||||
Large
|
||||
}
|
||||
|
||||
53
Radzen.Blazor/AlertStyle.cs
Normal file
53
Radzen.Blazor/AlertStyle.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
namespace Radzen;
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the display style or severity of a <see cref="Radzen.Blazor.RadzenAlert" />. Affects the visual styling of RadzenAlert (background and text color).
|
||||
/// </summary>
|
||||
public enum AlertStyle
|
||||
{
|
||||
/// <summary>
|
||||
/// Primary styling. Similar to primary buttons.
|
||||
/// </summary>
|
||||
Primary,
|
||||
|
||||
/// <summary>
|
||||
/// Secondary styling. Similar to secondary buttons.
|
||||
/// </summary>
|
||||
Secondary,
|
||||
|
||||
/// <summary>
|
||||
/// Light styling. Similar to light buttons.
|
||||
/// </summary>
|
||||
Light,
|
||||
|
||||
/// <summary>
|
||||
/// Base styling. Similar to base buttons.
|
||||
/// </summary>
|
||||
Base,
|
||||
|
||||
/// <summary>
|
||||
/// Dark styling. Similar to dark buttons.
|
||||
/// </summary>
|
||||
Dark,
|
||||
|
||||
/// <summary>
|
||||
/// Success styling.
|
||||
/// </summary>
|
||||
Success,
|
||||
|
||||
/// <summary>
|
||||
/// Danger styling.
|
||||
/// </summary>
|
||||
Danger,
|
||||
|
||||
/// <summary>
|
||||
/// Warning styling.
|
||||
/// </summary>
|
||||
Warning,
|
||||
|
||||
/// <summary>
|
||||
/// Informative styling.
|
||||
/// </summary>
|
||||
Info
|
||||
}
|
||||
|
||||
33
Radzen.Blazor/AlignItems.cs
Normal file
33
Radzen.Blazor/AlignItems.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
namespace Radzen;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the alignment of Stack items.
|
||||
/// </summary>
|
||||
public enum AlignItems
|
||||
{
|
||||
/// <summary>
|
||||
/// Normal items alignment.
|
||||
/// </summary>
|
||||
Normal,
|
||||
|
||||
/// <summary>
|
||||
/// Center items alignment.
|
||||
/// </summary>
|
||||
Center,
|
||||
|
||||
/// <summary>
|
||||
/// Start items alignment.
|
||||
/// </summary>
|
||||
Start,
|
||||
|
||||
/// <summary>
|
||||
/// End items alignment.
|
||||
/// </summary>
|
||||
End,
|
||||
|
||||
/// <summary>
|
||||
/// Stretch items alignment.
|
||||
/// </summary>
|
||||
Stretch
|
||||
}
|
||||
|
||||
@@ -22,19 +22,19 @@ namespace Radzen.Blazor
|
||||
/// Gets or sets the text of the appointment.
|
||||
/// </summary>
|
||||
/// <value>The text.</value>
|
||||
public string Text { get; set; }
|
||||
public string? Text { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the data associated with the appointment
|
||||
/// </summary>
|
||||
/// <value>The data.</value>
|
||||
public object Data { get; set; }
|
||||
public object? Data { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified object is equal to this instance. Used to check if two appointments are equal.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object to compare with this instance.</param>
|
||||
/// <returns><c>true</c> if the specified is equal to this instance; otherwise, <c>false</c>.</returns>
|
||||
public override bool Equals(object obj)
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
return obj is AppointmentData data &&
|
||||
Start == data.Start &&
|
||||
|
||||
@@ -1,17 +1,12 @@
|
||||
namespace Radzen.Blazor
|
||||
{
|
||||
/// <summary>
|
||||
/// The <c>AutomCompleteType</c> is a string-associated enum of
|
||||
/// browser-supported autocomplete attribute values.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This class lists the autocomplete attirbute options allowing
|
||||
/// developers to provide the browser with guidance on how to pre-populate
|
||||
/// the form fields. It is a class rather than a simpler enum to associate
|
||||
/// each option with the string the browser expects. For more information
|
||||
/// please review the list of options (https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete)
|
||||
/// The <c>AutomCompleteType</c> is a string-associated enum of browser-supported autocomplete attribute values.
|
||||
/// Lists the autocomplete attribute options allowing developers to provide the browser with guidance on how to pre-populate the form fields.
|
||||
/// It is a class rather than a simpler enum to associate each option with the string the browser expects.
|
||||
/// For more information please review the list of options (https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete)
|
||||
/// and the spec (https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#autofill).
|
||||
/// </remarks>
|
||||
/// </summary>
|
||||
public enum AutoCompleteType
|
||||
{
|
||||
/// <summary>Autocomplete is disabled. </summary>
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace Radzen.Blazor
|
||||
/// </summary>
|
||||
/// <value>The stroke.</value>
|
||||
[Parameter]
|
||||
public string Stroke { get; set; }
|
||||
public string? Stroke { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the pixel width of axis.
|
||||
/// </summary>
|
||||
@@ -26,21 +26,21 @@ namespace Radzen.Blazor
|
||||
/// </summary>
|
||||
/// <value>The child content.</value>
|
||||
[Parameter]
|
||||
public RenderFragment ChildContent { get; set; }
|
||||
public RenderFragment? ChildContent { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the format string used to display the axis values.
|
||||
/// </summary>
|
||||
/// <value>The format string.</value>
|
||||
[Parameter]
|
||||
public string FormatString { get; set; }
|
||||
public string? FormatString { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a formatter function that formats the axis values.
|
||||
/// </summary>
|
||||
/// <value>The formatter.</value>
|
||||
[Parameter]
|
||||
public Func<object, string> Formatter { get; set; }
|
||||
public Func<object, string>? Formatter { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the type of the line used to display the axis.
|
||||
@@ -80,20 +80,20 @@ namespace Radzen.Blazor
|
||||
/// </summary>
|
||||
/// <value>The minimum.</value>
|
||||
[Parameter]
|
||||
public object Min { get; set; }
|
||||
public object? Min { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the maximum value of the axis.
|
||||
/// </summary>
|
||||
/// <value>The maximum.</value>
|
||||
[Parameter]
|
||||
public object Max { get; set; }
|
||||
public object? Max { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the step of the axis.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public object Step { get; set; }
|
||||
public object? Step { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this <see cref="AxisBase"/> is visible.
|
||||
@@ -139,7 +139,7 @@ namespace Radzen.Blazor
|
||||
}
|
||||
else
|
||||
{
|
||||
return scale.FormatTick(FormatString, value);
|
||||
return scale.FormatTick(FormatString ?? string.Empty, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
53
Radzen.Blazor/BadgeStyle.cs
Normal file
53
Radzen.Blazor/BadgeStyle.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
namespace Radzen;
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the display style of a <see cref="Radzen.Blazor.RadzenBadge" />. Affects the visual styling of RadzenBadge (background and text color).
|
||||
/// </summary>
|
||||
public enum BadgeStyle
|
||||
{
|
||||
/// <summary>
|
||||
/// Primary styling. Similar to primary buttons.
|
||||
/// </summary>
|
||||
Primary,
|
||||
|
||||
/// <summary>
|
||||
/// Secondary styling. Similar to secondary buttons.
|
||||
/// </summary>
|
||||
Secondary,
|
||||
|
||||
/// <summary>
|
||||
/// Light styling. Similar to light buttons.
|
||||
/// </summary>
|
||||
Light,
|
||||
|
||||
/// <summary>
|
||||
/// Base styling. Similar to base buttons.
|
||||
/// </summary>
|
||||
Base,
|
||||
|
||||
/// <summary>
|
||||
/// Dark styling. Similar to dark buttons.
|
||||
/// </summary>
|
||||
Dark,
|
||||
|
||||
/// <summary>
|
||||
/// Success styling.
|
||||
/// </summary>
|
||||
Success,
|
||||
|
||||
/// <summary>
|
||||
/// Danger styling.
|
||||
/// </summary>
|
||||
Danger,
|
||||
|
||||
/// <summary>
|
||||
/// Warning styling.
|
||||
/// </summary>
|
||||
Warning,
|
||||
|
||||
/// <summary>
|
||||
/// Informative styling.
|
||||
/// </summary>
|
||||
Info
|
||||
}
|
||||
|
||||
28
Radzen.Blazor/ButtonSize.cs
Normal file
28
Radzen.Blazor/ButtonSize.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
namespace Radzen;
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the size of a <see cref="Radzen.Blazor.RadzenButton" />.
|
||||
/// </summary>
|
||||
public enum ButtonSize
|
||||
{
|
||||
/// <summary>
|
||||
/// The default size of a button.
|
||||
/// </summary>
|
||||
Medium,
|
||||
|
||||
/// <summary>
|
||||
/// A button larger than the default.
|
||||
/// </summary>
|
||||
Large,
|
||||
|
||||
/// <summary>
|
||||
/// A button smaller than the default.
|
||||
/// </summary>
|
||||
Small,
|
||||
|
||||
/// <summary>
|
||||
/// The smallest button.
|
||||
/// </summary>
|
||||
ExtraSmall
|
||||
}
|
||||
|
||||
53
Radzen.Blazor/ButtonStyle.cs
Normal file
53
Radzen.Blazor/ButtonStyle.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
namespace Radzen;
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the display style of a <see cref="Radzen.Blazor.RadzenButton" />. Affects the visual styling of RadzenButton (background and text color).
|
||||
/// </summary>
|
||||
public enum ButtonStyle
|
||||
{
|
||||
/// <summary>
|
||||
/// A primary button. Clicking it performs the primary action in a form or dialog (e.g. "save").
|
||||
/// </summary>
|
||||
Primary,
|
||||
|
||||
/// <summary>
|
||||
/// A secondary button. Clicking it performs a secondary action in a form or dialog (e.g. close a dialog or cancel a form).
|
||||
/// </summary>
|
||||
Secondary,
|
||||
|
||||
/// <summary>
|
||||
/// A button with lighter styling.
|
||||
/// </summary>
|
||||
Light,
|
||||
|
||||
/// <summary>
|
||||
/// The base UI styling.
|
||||
/// </summary>
|
||||
Base,
|
||||
|
||||
/// <summary>
|
||||
/// A button with dark styling.
|
||||
/// </summary>
|
||||
Dark,
|
||||
|
||||
/// <summary>
|
||||
/// A button with success styling.
|
||||
/// </summary>
|
||||
Success,
|
||||
|
||||
/// <summary>
|
||||
/// A button which represents a dangerous action e.g. "delete".
|
||||
/// </summary>
|
||||
Danger,
|
||||
|
||||
/// <summary>
|
||||
/// A button with warning styling.
|
||||
/// </summary>
|
||||
Warning,
|
||||
|
||||
/// <summary>
|
||||
/// A button with informative styling.
|
||||
/// </summary>
|
||||
Info
|
||||
}
|
||||
|
||||
23
Radzen.Blazor/ButtonType.cs
Normal file
23
Radzen.Blazor/ButtonType.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
namespace Radzen;
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the type of a <see cref="Radzen.Blazor.RadzenButton" />. Renders as the <c>type</c> HTML attribute.
|
||||
/// </summary>
|
||||
public enum ButtonType
|
||||
{
|
||||
/// <summary>
|
||||
/// Generic button which does not submit its parent form.
|
||||
/// </summary>
|
||||
Button,
|
||||
|
||||
/// <summary>
|
||||
/// Clicking a submit button submits its parent form.
|
||||
/// </summary>
|
||||
Submit,
|
||||
|
||||
/// <summary>
|
||||
/// Clicking a reset button clears the value of all inputs in its parent form.
|
||||
/// </summary>
|
||||
Reset
|
||||
}
|
||||
|
||||
@@ -19,7 +19,17 @@ namespace Radzen.Blazor
|
||||
/// Cache for the value returned by <see cref="Category"/> when that value is only dependent on
|
||||
/// <see cref="CategoryProperty"/>.
|
||||
/// </summary>
|
||||
Func<TItem, double> categoryPropertyCache;
|
||||
Func<TItem, double>? categoryPropertyCache;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the parent <see cref="RadzenChart"/> instance or throws an <see cref="InvalidOperationException"/> if not present.
|
||||
/// </summary>
|
||||
/// <returns>The parent <see cref="RadzenChart"/>.</returns>
|
||||
/// <exception cref="InvalidOperationException">Thrown when the parent chart is not set.</exception>
|
||||
protected RadzenChart RequireChart()
|
||||
{
|
||||
return Chart ?? throw new InvalidOperationException($"{GetType().Name} requires a parent RadzenChart.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a getter function that returns a value from the specified category scale for the specified data item.
|
||||
@@ -34,13 +44,13 @@ namespace Radzen.Blazor
|
||||
|
||||
if (IsNumeric(CategoryProperty))
|
||||
{
|
||||
categoryPropertyCache = PropertyAccess.Getter<TItem, double>(CategoryProperty);
|
||||
categoryPropertyCache = PropertyAccess.Getter<TItem, double>(CategoryProperty!);
|
||||
return categoryPropertyCache;
|
||||
}
|
||||
|
||||
if (IsDate(CategoryProperty))
|
||||
{
|
||||
var category = PropertyAccess.Getter<TItem, DateTime>(CategoryProperty);
|
||||
var category = PropertyAccess.Getter<TItem, DateTime>(CategoryProperty!);
|
||||
categoryPropertyCache = (item) => category(item).Ticks;
|
||||
return categoryPropertyCache;
|
||||
}
|
||||
@@ -49,7 +59,7 @@ namespace Radzen.Blazor
|
||||
{
|
||||
Func<TItem, object> category = String.IsNullOrEmpty(CategoryProperty) ? (item) => string.Empty : PropertyAccess.Getter<TItem, object>(CategoryProperty);
|
||||
|
||||
return (item) => ordinal.Data.IndexOf(category(item));
|
||||
return (item) => ordinal.Data?.IndexOf(category(item)) ?? -1;
|
||||
}
|
||||
|
||||
return (item) => Items.IndexOf(item);
|
||||
@@ -60,6 +70,8 @@ namespace Radzen.Blazor
|
||||
/// </summary>
|
||||
protected Func<TItem, double> ComposeCategory(ScaleBase scale)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(scale);
|
||||
|
||||
return scale.Compose(Category(scale));
|
||||
}
|
||||
|
||||
@@ -68,6 +80,8 @@ namespace Radzen.Blazor
|
||||
/// </summary>
|
||||
protected Func<TItem, double> ComposeValue(ScaleBase scale)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(scale);
|
||||
|
||||
return scale.Compose(Value);
|
||||
}
|
||||
|
||||
@@ -77,7 +91,7 @@ namespace Radzen.Blazor
|
||||
/// <param name="propertyName">Name of the property.</param>
|
||||
/// <returns><c>true</c> if the specified property name is date; otherwise, <c>false</c>.</returns>
|
||||
/// <exception cref="ArgumentException">Property {propertyName} does not exist</exception>
|
||||
protected bool IsDate(string propertyName)
|
||||
protected bool IsDate(string? propertyName)
|
||||
{
|
||||
if (String.IsNullOrEmpty(propertyName))
|
||||
{
|
||||
@@ -106,7 +120,7 @@ namespace Radzen.Blazor
|
||||
/// <param name="propertyName">Name of the property.</param>
|
||||
/// <returns><c>true</c> if the specified property name is numeric; otherwise, <c>false</c>.</returns>
|
||||
/// <exception cref="ArgumentException">Property {propertyName} does not exist</exception>
|
||||
protected bool IsNumeric(string propertyName)
|
||||
protected bool IsNumeric(string? propertyName)
|
||||
{
|
||||
if (String.IsNullOrEmpty(propertyName))
|
||||
{
|
||||
@@ -125,21 +139,21 @@ namespace Radzen.Blazor
|
||||
|
||||
/// <inheritdoc />
|
||||
[Parameter]
|
||||
public string Title { get; set; }
|
||||
public string Title { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the child content.
|
||||
/// </summary>
|
||||
/// <value>The child content.</value>
|
||||
[Parameter]
|
||||
public RenderFragment ChildContent { get; set; }
|
||||
public RenderFragment? ChildContent { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the tooltip template.
|
||||
/// </summary>
|
||||
/// <value>The tooltip template.</value>
|
||||
[Parameter]
|
||||
public RenderFragment<TItem> TooltipTemplate { get; set; }
|
||||
public RenderFragment<TItem>? TooltipTemplate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of overlays.
|
||||
@@ -157,7 +171,7 @@ namespace Radzen.Blazor
|
||||
/// The name of the property of <typeparamref name="TItem" /> that provides the X axis (a.k.a. category axis) values.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public string CategoryProperty { get; set; }
|
||||
public string? CategoryProperty { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this <see cref="CartesianSeries{TItem}"/> is visible.
|
||||
@@ -194,7 +208,7 @@ namespace Radzen.Blazor
|
||||
/// The name of the property of <typeparamref name="TItem" /> that provides the Y axis (a.k.a. value axis) values.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public string ValueProperty { get; set; }
|
||||
public string? ValueProperty { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
[Parameter]
|
||||
@@ -223,7 +237,7 @@ namespace Radzen.Blazor
|
||||
/// </summary>
|
||||
/// <value>The data.</value>
|
||||
[Parameter]
|
||||
public IEnumerable<TItem> Data { get; set; }
|
||||
public IEnumerable<TItem>? Data { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Stores <see cref="Data" /> as an IList of <typeparamref name="TItem"/>.
|
||||
@@ -272,6 +286,8 @@ namespace Radzen.Blazor
|
||||
/// <inheritdoc />
|
||||
public virtual ScaleBase TransformCategoryScale(ScaleBase scale)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(scale);
|
||||
|
||||
if (Items == null)
|
||||
{
|
||||
return scale;
|
||||
@@ -298,7 +314,7 @@ namespace Radzen.Blazor
|
||||
|
||||
var data = GetCategories();
|
||||
|
||||
if (scale is OrdinalScale ordinal)
|
||||
if (scale is OrdinalScale ordinal && ordinal.Data != null)
|
||||
{
|
||||
foreach (var item in ordinal.Data)
|
||||
{
|
||||
@@ -328,6 +344,8 @@ namespace Radzen.Blazor
|
||||
/// <inheritdoc />
|
||||
public virtual ScaleBase TransformValueScale(ScaleBase scale)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(scale);
|
||||
|
||||
if (Items != null)
|
||||
{
|
||||
if (Items.Any())
|
||||
@@ -398,29 +416,32 @@ namespace Radzen.Blazor
|
||||
{
|
||||
if (Data != null)
|
||||
{
|
||||
if (Data is IList<TItem>)
|
||||
if (Data is IList<TItem> list)
|
||||
{
|
||||
Items = Data as IList<TItem>;
|
||||
Items = list;
|
||||
}
|
||||
else
|
||||
{
|
||||
Items = Data.ToList();
|
||||
}
|
||||
|
||||
if (IsDate(CategoryProperty) || IsNumeric(CategoryProperty))
|
||||
if (!string.IsNullOrEmpty(CategoryProperty) && (IsDate(CategoryProperty) || IsNumeric(CategoryProperty)))
|
||||
{
|
||||
Items = Items.AsQueryable().OrderBy(CategoryProperty).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
await Chart.Refresh(false);
|
||||
if (Chart != null)
|
||||
{
|
||||
await Chart.Refresh(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Initialize()
|
||||
{
|
||||
Chart.AddSeries(this);
|
||||
Chart?.AddSeries(this);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -443,6 +464,9 @@ namespace Radzen.Blazor
|
||||
/// <returns><c>true</c> if the polygon contains the point, <c>false</c> otherwise.</returns>
|
||||
protected bool InsidePolygon(Point point, Point[] polygon)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(point);
|
||||
ArgumentNullException.ThrowIfNull(polygon);
|
||||
|
||||
var minX = polygon[0].X;
|
||||
var maxX = polygon[0].X;
|
||||
var minY = polygon[0].Y;
|
||||
@@ -479,18 +503,22 @@ namespace Radzen.Blazor
|
||||
/// <inheritdoc />
|
||||
public virtual RenderFragment RenderTooltip(object data)
|
||||
{
|
||||
var chart = RequireChart();
|
||||
var item = (TItem)data;
|
||||
|
||||
|
||||
return builder =>
|
||||
{
|
||||
if (Chart.Tooltip.Shared)
|
||||
if (chart.Tooltip.Shared)
|
||||
{
|
||||
var category = PropertyAccess.GetValue(item, CategoryProperty);
|
||||
builder.OpenComponent<ChartSharedTooltip>(0);
|
||||
builder.AddAttribute(1, nameof(ChartSharedTooltip.Class), TooltipClass(item));
|
||||
builder.AddAttribute(2, nameof(ChartSharedTooltip.Title), TooltipTitle(item));
|
||||
builder.AddAttribute(3, nameof(ChartSharedTooltip.ChildContent), RenderSharedTooltipItems(category));
|
||||
builder.CloseComponent();
|
||||
var category = !string.IsNullOrEmpty(CategoryProperty) ? PropertyAccess.GetValue(item, CategoryProperty) : null;
|
||||
if (category != null)
|
||||
{
|
||||
builder.OpenComponent<ChartSharedTooltip>(0);
|
||||
builder.AddAttribute(1, nameof(ChartSharedTooltip.Class), TooltipClass(item));
|
||||
builder.AddAttribute(2, nameof(ChartSharedTooltip.Title), TooltipTitle(item));
|
||||
builder.AddAttribute(3, nameof(ChartSharedTooltip.ChildContent), RenderSharedTooltipItems(category));
|
||||
builder.CloseComponent();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -508,9 +536,11 @@ namespace Radzen.Blazor
|
||||
|
||||
private RenderFragment RenderSharedTooltipItems(object category)
|
||||
{
|
||||
var chart = RequireChart();
|
||||
|
||||
return builder =>
|
||||
{
|
||||
var visibleSeries = Chart.Series.Where(s => s.Visible).ToList();
|
||||
var visibleSeries = chart.Series.Where(s => s.Visible).ToList();
|
||||
|
||||
foreach (var series in visibleSeries)
|
||||
{
|
||||
@@ -524,7 +554,7 @@ namespace Radzen.Blazor
|
||||
{
|
||||
return builder =>
|
||||
{
|
||||
var item = Items.FirstOrDefault(i => object.Equals(PropertyAccess.GetValue(i, CategoryProperty), category));
|
||||
var item = Items.FirstOrDefault(i => !string.IsNullOrEmpty(CategoryProperty) && object.Equals(PropertyAccess.GetValue(i, CategoryProperty), category));
|
||||
|
||||
if (item != null)
|
||||
{
|
||||
@@ -553,7 +583,7 @@ namespace Radzen.Blazor
|
||||
/// <param name="item">The item.</param>
|
||||
protected virtual string TooltipStyle(TItem item)
|
||||
{
|
||||
return Chart.Tooltip.Style;
|
||||
return Chart?.Tooltip?.Style ?? string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -562,7 +592,13 @@ namespace Radzen.Blazor
|
||||
/// <param name="item">The item.</param>
|
||||
protected virtual string TooltipClass(TItem item)
|
||||
{
|
||||
return $"rz-series-{Chart.Series.IndexOf(this)}-tooltip";
|
||||
var chart = Chart;
|
||||
if (chart == null)
|
||||
{
|
||||
return "rz-series-tooltip";
|
||||
}
|
||||
|
||||
return $"rz-series-{chart.Series.IndexOf(this)}-tooltip";
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -576,6 +612,8 @@ namespace Radzen.Blazor
|
||||
/// </summary>
|
||||
protected virtual RenderFragment RenderLegendItem(bool clickable)
|
||||
{
|
||||
var chart = RequireChart();
|
||||
var index = chart.Series.IndexOf(this);
|
||||
var style = new List<string>();
|
||||
|
||||
if (IsVisible == false)
|
||||
@@ -586,7 +624,7 @@ namespace Radzen.Blazor
|
||||
return builder =>
|
||||
{
|
||||
builder.OpenComponent<LegendItem>(0);
|
||||
builder.AddAttribute(1, nameof(LegendItem.Index), Chart.Series.IndexOf(this));
|
||||
builder.AddAttribute(1, nameof(LegendItem.Index), index);
|
||||
builder.AddAttribute(2, nameof(LegendItem.Color), Color);
|
||||
builder.AddAttribute(3, nameof(LegendItem.MarkerType), MarkerType);
|
||||
builder.AddAttribute(4, nameof(LegendItem.Style), string.Join(";", style));
|
||||
@@ -617,19 +655,35 @@ namespace Radzen.Blazor
|
||||
/// <inheritdoc />
|
||||
public double GetMedian()
|
||||
{
|
||||
return Data.Select(e => Value(e)).OrderBy(e => e).Skip(Data.Count() / 2).FirstOrDefault();
|
||||
var values = Items.Select(Value).OrderBy(e => e).ToList();
|
||||
if (values.Count == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return values[values.Count / 2];
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public double GetMean()
|
||||
{
|
||||
return Data.Select(e => Value(e)).DefaultIfEmpty(double.NaN).Average();
|
||||
return Items.Any() ? Items.Select(Value).Average() : double.NaN;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public double GetMode()
|
||||
{
|
||||
return Data.Any() ? Data.GroupBy(e => Value(e)).Select(g => new { Value = g.Key, Count = g.Count() }).OrderByDescending(e => e.Count).FirstOrDefault().Value : double.NaN;
|
||||
if (!Items.Any())
|
||||
{
|
||||
return double.NaN;
|
||||
}
|
||||
|
||||
return Items
|
||||
.GroupBy(item => Value(item))
|
||||
.Select(g => new { Value = g.Key, Count = g.Count() })
|
||||
.OrderByDescending(e => e.Count)
|
||||
.First()
|
||||
.Value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -639,33 +693,43 @@ namespace Radzen.Blazor
|
||||
{
|
||||
double a = double.NaN, b = double.NaN;
|
||||
|
||||
if (Data.Any())
|
||||
var chart = Chart;
|
||||
if (chart == null)
|
||||
{
|
||||
return (a, b);
|
||||
}
|
||||
|
||||
if (Items.Any())
|
||||
{
|
||||
Func<TItem, double> X;
|
||||
Func<TItem, double> Y;
|
||||
if (Chart.ShouldInvertAxes())
|
||||
if (chart.ShouldInvertAxes())
|
||||
{
|
||||
X = e => Chart.CategoryScale.Scale(Value(e));
|
||||
Y = e => Chart.ValueScale.Scale(Category(Chart.ValueScale)(e));
|
||||
var valueScale = chart.ValueScale;
|
||||
var categoryAccessor = Category(chart.ValueScale);
|
||||
X = e => chart.CategoryScale.Scale(Value(e));
|
||||
Y = e => valueScale.Scale(categoryAccessor(e));
|
||||
}
|
||||
else
|
||||
{
|
||||
X = e => Chart.CategoryScale.Scale(Category(Chart.CategoryScale)(e));
|
||||
Y = e => Chart.ValueScale.Scale(Value(e));
|
||||
var categoryAccessor = Category(chart.CategoryScale);
|
||||
X = e => chart.CategoryScale.Scale(categoryAccessor(e));
|
||||
Y = e => chart.ValueScale.Scale(Value(e));
|
||||
}
|
||||
|
||||
var avgX = Data.Select(e => X(e)).Average();
|
||||
var avgY = Data.Select(e => Y(e)).Average();
|
||||
var sumXY = Data.Sum(e => (X(e) - avgX) * (Y(e) - avgY));
|
||||
if (Chart.ShouldInvertAxes())
|
||||
var data = Items.ToList();
|
||||
var avgX = data.Select(e => X(e)).Average();
|
||||
var avgY = data.Select(e => Y(e)).Average();
|
||||
var sumXY = data.Sum(e => (X(e) - avgX) * (Y(e) - avgY));
|
||||
if (chart.ShouldInvertAxes())
|
||||
{
|
||||
var sumYSq = Data.Sum(e => (Y(e) - avgY) * (Y(e) - avgY));
|
||||
var sumYSq = data.Sum(e => (Y(e) - avgY) * (Y(e) - avgY));
|
||||
b = sumXY / sumYSq;
|
||||
a = avgX - b * avgY;
|
||||
}
|
||||
else
|
||||
{
|
||||
var sumXSq = Data.Sum(e => (X(e) - avgX) * (X(e) - avgX));
|
||||
var sumXSq = data.Sum(e => (X(e) - avgX) * (X(e) - avgX));
|
||||
b = sumXY / sumXSq;
|
||||
a = avgY - b * avgX;
|
||||
}
|
||||
@@ -678,7 +742,9 @@ namespace Radzen.Blazor
|
||||
{
|
||||
IsVisible = !IsVisible;
|
||||
|
||||
if (Chart.LegendClick.HasDelegate)
|
||||
var chart = Chart;
|
||||
|
||||
if (chart?.LegendClick.HasDelegate == true)
|
||||
{
|
||||
var args = new LegendClickEventArgs
|
||||
{
|
||||
@@ -687,18 +753,28 @@ namespace Radzen.Blazor
|
||||
IsVisible = IsVisible,
|
||||
};
|
||||
|
||||
await Chart.LegendClick.InvokeAsync(args);
|
||||
await chart.LegendClick.InvokeAsync(args);
|
||||
|
||||
IsVisible = args.IsVisible;
|
||||
}
|
||||
|
||||
await Chart.Refresh();
|
||||
if (chart != null)
|
||||
{
|
||||
await chart.Refresh();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string GetTitle()
|
||||
{
|
||||
return String.IsNullOrEmpty(Title) ? $"Series {Chart.Series.IndexOf(this) + 1}" : Title;
|
||||
var chart = Chart;
|
||||
if (string.IsNullOrEmpty(Title))
|
||||
{
|
||||
var index = chart?.Series.IndexOf(this) ?? 0;
|
||||
return $"Series {index + 1}";
|
||||
}
|
||||
|
||||
return Title;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -716,8 +792,9 @@ namespace Radzen.Blazor
|
||||
/// <param name="item">The item.</param>
|
||||
protected virtual string TooltipTitle(TItem item)
|
||||
{
|
||||
var category = Category(Chart.CategoryScale);
|
||||
return Chart.CategoryAxis.Format(Chart.CategoryScale, Chart.CategoryScale.Value(category(item)));
|
||||
var chart = RequireChart();
|
||||
var category = Category(chart.CategoryScale);
|
||||
return chart.CategoryAxis.Format(chart.CategoryScale, chart.CategoryScale.Value(category(item)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -727,7 +804,8 @@ namespace Radzen.Blazor
|
||||
/// <returns>System.String.</returns>
|
||||
protected virtual string TooltipValue(TItem item)
|
||||
{
|
||||
return Chart.ValueAxis.Format(Chart.ValueScale, Chart.ValueScale.Value(Value(item)));
|
||||
var chart = RequireChart();
|
||||
return chart.ValueAxis.Format(chart.ValueScale, chart.ValueScale.Value(Value(item)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -736,8 +814,9 @@ namespace Radzen.Blazor
|
||||
/// <param name="item">The item.</param>
|
||||
internal virtual double TooltipX(TItem item)
|
||||
{
|
||||
var category = Category(Chart.CategoryScale);
|
||||
return Chart.CategoryScale.Scale(category(item), true);
|
||||
var chart = RequireChart();
|
||||
var category = Category(chart.CategoryScale);
|
||||
return chart.CategoryScale.Scale(category(item), true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -746,7 +825,8 @@ namespace Radzen.Blazor
|
||||
/// <param name="item">The item.</param>
|
||||
internal virtual double TooltipY(TItem item)
|
||||
{
|
||||
return Chart.ValueScale.Scale(Value(item), true);
|
||||
var chart = RequireChart();
|
||||
return chart.ValueScale.Scale(Value(item), true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -760,25 +840,26 @@ namespace Radzen.Blazor
|
||||
return new { Item = item, Distance = distance };
|
||||
}).Aggregate((a, b) => a.Distance < b.Distance ? a : b).Item;
|
||||
|
||||
return (retObject,
|
||||
return (retObject!,
|
||||
new Point() { X = TooltipX(retObject), Y = TooltipY(retObject)});
|
||||
}
|
||||
|
||||
return (null, null);
|
||||
return (default!, new Point());
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual IEnumerable<ChartDataLabel> GetDataLabels(double offsetX, double offsetY)
|
||||
{
|
||||
var chart = RequireChart();
|
||||
var list = new List<ChartDataLabel>();
|
||||
|
||||
foreach (var d in Data)
|
||||
foreach (var d in Items)
|
||||
{
|
||||
list.Add(new ChartDataLabel
|
||||
{
|
||||
Position = new Point { X = TooltipX(d) + offsetX, Y = TooltipY(d) + offsetY },
|
||||
TextAnchor = "middle",
|
||||
Text = Chart.ValueAxis.Format(Chart.ValueScale, Value(d))
|
||||
Text = chart.ValueAxis.Format(chart.ValueScale, Value(d))
|
||||
});
|
||||
}
|
||||
|
||||
@@ -793,12 +874,12 @@ namespace Radzen.Blazor
|
||||
/// <param name="defaultValue">The default value.</param>
|
||||
/// <param name="colorRange">The color range value.</param>
|
||||
/// <param name="value">The value of the item.</param>
|
||||
protected string PickColor(int index, IEnumerable<string> colors, string defaultValue = null, IList<SeriesColorRange> colorRange = null, double value = 0.0)
|
||||
protected string? PickColor(int index, IEnumerable<string>? colors, string? defaultValue = null, IList<SeriesColorRange>? colorRange = null, double value = 0.0)
|
||||
{
|
||||
if (colorRange != null)
|
||||
{
|
||||
var result = colorRange.Where(r => r.Min <= value && r.Max >= value).FirstOrDefault<SeriesColorRange>();
|
||||
return result != null ? result.Color : defaultValue;
|
||||
return result?.Color ?? defaultValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -819,18 +900,20 @@ namespace Radzen.Blazor
|
||||
/// <inheritdoc />
|
||||
public async Task InvokeClick(EventCallback<SeriesClickEventArgs> handler, object data)
|
||||
{
|
||||
var category = Category(Chart.CategoryScale);
|
||||
var chart = RequireChart();
|
||||
var category = Category(chart.CategoryScale);
|
||||
var dataItem = (TItem)data;
|
||||
|
||||
await handler.InvokeAsync(new SeriesClickEventArgs
|
||||
{
|
||||
Data = data,
|
||||
Title = GetTitle(),
|
||||
Category = PropertyAccess.GetValue(data, CategoryProperty),
|
||||
Value = PropertyAccess.GetValue(data, ValueProperty),
|
||||
Category = !string.IsNullOrEmpty(CategoryProperty) ? PropertyAccess.GetValue(data, CategoryProperty) : null,
|
||||
Value = !string.IsNullOrEmpty(ValueProperty) ? PropertyAccess.GetValue(data, ValueProperty) : null,
|
||||
Point = new SeriesPoint
|
||||
{
|
||||
Category = category((TItem)data),
|
||||
Value = Value((TItem)data)
|
||||
Category = category(dataItem),
|
||||
Value = Value(dataItem)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
43
Radzen.Blazor/ChatMessage.cs
Normal file
43
Radzen.Blazor/ChatMessage.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
using System;
|
||||
|
||||
namespace Radzen.Blazor;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a chat message in the RadzenAIChat component.
|
||||
/// </summary>
|
||||
public class ChatMessage
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the unique identifier for the message.
|
||||
/// </summary>
|
||||
public string Id { get; set; } = Guid.NewGuid().ToString();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the content of the message.
|
||||
/// </summary>
|
||||
public string Content { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether this message is from the user.
|
||||
/// </summary>
|
||||
public bool IsUser { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the ID of the user who sent the message.
|
||||
/// </summary>
|
||||
public string UserId { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the timestamp when the message was created.
|
||||
/// </summary>
|
||||
public DateTime Timestamp { get; set; } = DateTime.Now;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether this message is currently streaming.
|
||||
/// </summary>
|
||||
public bool IsStreaming { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the role associated with the message (e.g., "user", "assistant").
|
||||
/// </summary>
|
||||
public string? Role { get; set; }
|
||||
}
|
||||
18
Radzen.Blazor/CollectionFilterMode.cs
Normal file
18
Radzen.Blazor/CollectionFilterMode.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
namespace Radzen;
|
||||
|
||||
/// <summary>
|
||||
/// Specifies how the filter should be applied to a collection of items.
|
||||
/// </summary>
|
||||
public enum CollectionFilterMode
|
||||
{
|
||||
/// <summary>
|
||||
/// The filter condition is satisfied if at least one item in the collection matches.
|
||||
/// </summary>
|
||||
Any,
|
||||
|
||||
/// <summary>
|
||||
/// The filter condition is satisfied only if all items in the collection match.
|
||||
/// </summary>
|
||||
All
|
||||
}
|
||||
|
||||
258
Radzen.Blazor/Colors.cs
Normal file
258
Radzen.Blazor/Colors.cs
Normal file
@@ -0,0 +1,258 @@
|
||||
namespace Radzen;
|
||||
|
||||
/// <summary>
|
||||
/// Colors.
|
||||
/// </summary>
|
||||
public static class Colors
|
||||
{
|
||||
/// <summary>
|
||||
/// Primary.
|
||||
/// </summary>
|
||||
public const string Primary = "var(--rz-primary)";
|
||||
|
||||
/// <summary>
|
||||
/// Primary lighter.
|
||||
/// </summary>
|
||||
public const string PrimaryLighter = "var(--rz-primary-lighter)";
|
||||
|
||||
/// <summary>
|
||||
/// Primary light.
|
||||
/// </summary>
|
||||
public const string PrimaryLight = "var(--rz-primary-light)";
|
||||
|
||||
/// <summary>
|
||||
/// Primary dark.
|
||||
/// </summary>
|
||||
public const string PrimaryDark = "var(--rz-primary-dark)";
|
||||
|
||||
/// <summary>
|
||||
/// Primary darker.
|
||||
/// </summary>
|
||||
public const string PrimaryDarker = "var(--rz-primary-darker)";
|
||||
|
||||
/// <summary>
|
||||
/// Secondary.
|
||||
/// </summary>
|
||||
public const string Secondary = "var(--rz-secondary)";
|
||||
|
||||
/// <summary>
|
||||
/// Secondary lighter.
|
||||
/// </summary>
|
||||
public const string SecondaryLighter = "var(--rz-secondary-lighter)";
|
||||
|
||||
/// <summary>
|
||||
/// Secondary light.
|
||||
/// </summary>
|
||||
public const string SecondaryLight = "var(--rz-secondary-light)";
|
||||
|
||||
/// <summary>
|
||||
/// Secondary dark.
|
||||
/// </summary>
|
||||
public const string SecondaryDark = "var(--rz-secondary-dark)";
|
||||
|
||||
/// <summary>
|
||||
/// Secondary darker.
|
||||
/// </summary>
|
||||
public const string SecondaryDarker = "var(--rz-secondary-darker)";
|
||||
|
||||
/// <summary>
|
||||
/// Info.
|
||||
/// </summary>
|
||||
public const string Info = "var(--rz-info)";
|
||||
|
||||
/// <summary>
|
||||
/// Info lighter.
|
||||
/// </summary>
|
||||
public const string InfoLighter = "var(--rz-info-lighter)";
|
||||
|
||||
/// <summary>
|
||||
/// Info light.
|
||||
/// </summary>
|
||||
public const string InfoLight = "var(--rz-info-light)";
|
||||
|
||||
/// <summary>
|
||||
/// Info dark.
|
||||
/// </summary>
|
||||
public const string InfoDark = "var(--rz-info-dark)";
|
||||
|
||||
/// <summary>
|
||||
/// Info darker.
|
||||
/// </summary>
|
||||
public const string InfoDarker = "var(--rz-info-darker)";
|
||||
|
||||
/// <summary>
|
||||
/// Success.
|
||||
/// </summary>
|
||||
public const string Success = "var(--rz-success)";
|
||||
|
||||
/// <summary>
|
||||
/// Success lighter.
|
||||
/// </summary>
|
||||
public const string SuccessLighter = "var(--rz-success-lighter)";
|
||||
|
||||
/// <summary>
|
||||
/// Success light.
|
||||
/// </summary>
|
||||
public const string SuccessLight = "var(--rz-success-light)";
|
||||
|
||||
/// <summary>
|
||||
/// Success dark.
|
||||
/// </summary>
|
||||
public const string SuccessDark = "var(--rz-success-dark)";
|
||||
|
||||
/// <summary>
|
||||
/// Success darker.
|
||||
/// </summary>
|
||||
public const string SuccessDarker = "var(--rz-success-darker)";
|
||||
|
||||
/// <summary>
|
||||
/// Warning.
|
||||
/// </summary>
|
||||
public const string Warning = "var(--rz-warning)";
|
||||
|
||||
/// <summary>
|
||||
/// Warning lighter.
|
||||
/// </summary>
|
||||
public const string WarningLighter = "var(--rz-warning-lighter)";
|
||||
|
||||
/// <summary>
|
||||
/// Warning light.
|
||||
/// </summary>
|
||||
public const string WarningLight = "var(--rz-warning-light)";
|
||||
|
||||
/// <summary>
|
||||
/// Warning dark.
|
||||
/// </summary>
|
||||
public const string WarningDark = "var(--rz-warning-dark)";
|
||||
|
||||
/// <summary>
|
||||
/// Warning darker.
|
||||
/// </summary>
|
||||
public const string WarningDarker = "var(--rz-warning-darker)";
|
||||
|
||||
/// <summary>
|
||||
/// Danger.
|
||||
/// </summary>
|
||||
public const string Danger = "var(--rz-danger)";
|
||||
|
||||
/// <summary>
|
||||
/// Danger lighter.
|
||||
/// </summary>
|
||||
public const string DangerLighter = "var(--rz-danger-lighter)";
|
||||
|
||||
/// <summary>
|
||||
/// Danger light.
|
||||
/// </summary>
|
||||
public const string DangerLight = "var(--rz-danger-light)";
|
||||
|
||||
/// <summary>
|
||||
/// Danger dark.
|
||||
/// </summary>
|
||||
public const string DangerDark = "var(--rz-danger-dark)";
|
||||
|
||||
/// <summary>
|
||||
/// Danger darker.
|
||||
/// </summary>
|
||||
public const string DangerDarker = "var(--rz-danger-darker)";
|
||||
|
||||
/// <summary>
|
||||
/// White.
|
||||
/// </summary>
|
||||
public const string White = "var(--rz-white)";
|
||||
|
||||
/// <summary>
|
||||
/// Black.
|
||||
/// </summary>
|
||||
public const string Black = "var(--rz-black)";
|
||||
|
||||
/// <summary>
|
||||
/// Base 50.
|
||||
/// </summary>
|
||||
public const string Base50 = "var(--rz-base-50)";
|
||||
|
||||
/// <summary>
|
||||
/// Base 100.
|
||||
/// </summary>
|
||||
public const string Base100 = "var(--rz-base-100)";
|
||||
|
||||
/// <summary>
|
||||
/// Base 200.
|
||||
/// </summary>
|
||||
public const string Base200 = "var(--rz-base-200)";
|
||||
|
||||
/// <summary>
|
||||
/// Base 300.
|
||||
/// </summary>
|
||||
public const string Base300 = "var(--rz-base-300)";
|
||||
|
||||
/// <summary>
|
||||
/// Base 400.
|
||||
/// </summary>
|
||||
public const string Base400 = "var(--rz-base-400)";
|
||||
|
||||
/// <summary>
|
||||
/// Base 500.
|
||||
/// </summary>
|
||||
public const string Base500 = "var(--rz-base-500)";
|
||||
|
||||
/// <summary>
|
||||
/// Base 600.
|
||||
/// </summary>
|
||||
public const string Base600 = "var(--rz-base-600)";
|
||||
|
||||
/// <summary>
|
||||
/// Base 700.
|
||||
/// </summary>
|
||||
public const string Base700 = "var(--rz-base-700)";
|
||||
|
||||
/// <summary>
|
||||
/// Base 800.
|
||||
/// </summary>
|
||||
public const string Base800 = "var(--rz-base-800)";
|
||||
|
||||
/// <summary>
|
||||
/// Base 900.
|
||||
/// </summary>
|
||||
public const string Base900 = "var(--rz-base-900)";
|
||||
|
||||
/// <summary>
|
||||
/// Series1.
|
||||
/// </summary>
|
||||
public const string Series1 = "var(--rz-series-1)";
|
||||
|
||||
/// <summary>
|
||||
/// Series2.
|
||||
/// </summary>
|
||||
public const string Series2 = "var(--rz-series-2)";
|
||||
|
||||
/// <summary>
|
||||
/// Series3.
|
||||
/// </summary>
|
||||
public const string Series3 = "var(--rz-series-3)";
|
||||
|
||||
/// <summary>
|
||||
/// Series4.
|
||||
/// </summary>
|
||||
public const string Series4 = "var(--rz-series-4)";
|
||||
|
||||
/// <summary>
|
||||
/// Series5.
|
||||
/// </summary>
|
||||
public const string Series5 = "var(--rz-series-5)";
|
||||
|
||||
/// <summary>
|
||||
/// Series6.
|
||||
/// </summary>
|
||||
public const string Series6 = "var(--rz-series-6)";
|
||||
|
||||
/// <summary>
|
||||
/// Series7.
|
||||
/// </summary>
|
||||
public const string Series7 = "var(--rz-series-7)";
|
||||
|
||||
/// <summary>
|
||||
/// Series8.
|
||||
/// </summary>
|
||||
public const string Series8 = "var(--rz-series-8)";
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
53
Radzen.Blazor/CompositeFilterDescriptor.cs
Normal file
53
Radzen.Blazor/CompositeFilterDescriptor.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Radzen;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a filter in a component that supports filtering.
|
||||
/// </summary>
|
||||
public class CompositeFilterDescriptor
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the filtered property.
|
||||
/// </summary>
|
||||
/// <value>The property.</value>
|
||||
public string? Property { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the property type.
|
||||
/// </summary>
|
||||
/// <value>The property type.</value>
|
||||
public Type? Type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the filtered property.
|
||||
/// </summary>
|
||||
/// <value>The property.</value>
|
||||
public string? FilterProperty { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the value to filter by.
|
||||
/// </summary>
|
||||
/// <value>The filter value.</value>
|
||||
public object? FilterValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the operator which will compare the property value with <see cref="FilterValue" />.
|
||||
/// </summary>
|
||||
/// <value>The filter operator.</value>
|
||||
public FilterOperator? FilterOperator { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the logic used to combine the outcome of filtering by <see cref="FilterValue" />.
|
||||
/// </summary>
|
||||
/// <value>The logical filter operator.</value>
|
||||
public LogicalFilterOperator LogicalFilterOperator { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the filters.
|
||||
/// </summary>
|
||||
/// <value>The filters.</value>
|
||||
public IEnumerable<CompositeFilterDescriptor>? Filters { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,225 +1,227 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.Web;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Radzen
|
||||
{
|
||||
/// <summary>
|
||||
/// Class ContextMenuService. Contains various methods with options to open and close context menus.
|
||||
/// Should be added as scoped service in the application services and RadzenContextMenu should be added in application main layout.
|
||||
/// Implements the <see cref="IDisposable" />
|
||||
/// </summary>
|
||||
/// <seealso cref="IDisposable" />
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// @inject ContextMenuService ContextMenuService
|
||||
/// <RadzenButton Text="Show context menu" ContextMenu=@(args => ShowContextMenuWithContent(args)) />
|
||||
/// @code {
|
||||
/// void ShowContextMenuWithContent(MouseEventArgs args) => ContextMenuService.Open(args, ds =>
|
||||
/// @<RadzenMenu Click="OnMenuItemClick">
|
||||
/// <RadzenMenuItem Text="Item1" Value="1"></RadzenMenuItem>
|
||||
/// <RadzenMenuItem Text="Item2" Value="2"></RadzenMenuItem>
|
||||
/// <RadzenMenuItem Text="More items" Value="3">
|
||||
/// <RadzenMenuItem Text="More sub items" Value="4">
|
||||
/// <RadzenMenuItem Text="Item1" Value="5"></RadzenMenuItem>
|
||||
/// <RadzenMenuItem Text="Item2" Value="6"></RadzenMenuItem>
|
||||
/// </RadzenMenuItem>
|
||||
/// </RadzenMenuItem>
|
||||
/// </RadzenMenu>);
|
||||
///
|
||||
/// void OnMenuItemClick(MenuItemEventArgs args)
|
||||
/// {
|
||||
/// Console.WriteLine($"Menu item with Value={args.Value} clicked");
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
public class ContextMenuService : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the navigation manager.
|
||||
/// </summary>
|
||||
/// <value>The navigation manager.</value>
|
||||
NavigationManager navigationManager { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ContextMenuService"/> class.
|
||||
/// </summary>
|
||||
/// <param name="uriHelper">The URI helper.</param>
|
||||
public ContextMenuService(NavigationManager uriHelper)
|
||||
{
|
||||
navigationManager = uriHelper;
|
||||
|
||||
if (navigationManager != null)
|
||||
{
|
||||
navigationManager.LocationChanged += UriHelper_OnLocationChanged;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles the OnLocationChanged event of the UriHelper control.
|
||||
/// </summary>
|
||||
/// <param name="sender">The source of the event.</param>
|
||||
/// <param name="e">The <see cref="Microsoft.AspNetCore.Components.Routing.LocationChangedEventArgs"/> instance containing the event data.</param>
|
||||
private void UriHelper_OnLocationChanged(object sender, Microsoft.AspNetCore.Components.Routing.LocationChangedEventArgs e)
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.Web;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Radzen
|
||||
{
|
||||
/// <summary>
|
||||
/// Class ContextMenuService. Contains various methods with options to open and close context menus.
|
||||
/// Should be added as scoped service in the application services and RadzenContextMenu should be added in application main layout.
|
||||
/// Implements the <see cref="IDisposable" />
|
||||
/// </summary>
|
||||
/// <seealso cref="IDisposable" />
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// @inject ContextMenuService ContextMenuService
|
||||
/// <RadzenButton Text="Show context menu" ContextMenu=@(args => ShowContextMenuWithContent(args)) />
|
||||
/// @code {
|
||||
/// void ShowContextMenuWithContent(MouseEventArgs args) => ContextMenuService.Open(args, ds =>
|
||||
/// @<RadzenMenu Click="OnMenuItemClick">
|
||||
/// <RadzenMenuItem Text="Item1" Value="1"></RadzenMenuItem>
|
||||
/// <RadzenMenuItem Text="Item2" Value="2"></RadzenMenuItem>
|
||||
/// <RadzenMenuItem Text="More items" Value="3">
|
||||
/// <RadzenMenuItem Text="More sub items" Value="4">
|
||||
/// <RadzenMenuItem Text="Item1" Value="5"></RadzenMenuItem>
|
||||
/// <RadzenMenuItem Text="Item2" Value="6"></RadzenMenuItem>
|
||||
/// </RadzenMenuItem>
|
||||
/// </RadzenMenuItem>
|
||||
/// </RadzenMenu>);
|
||||
///
|
||||
/// void OnMenuItemClick(MenuItemEventArgs args)
|
||||
/// {
|
||||
/// Console.WriteLine($"Menu item with Value={args.Value} clicked");
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
public class ContextMenuService : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the navigation manager.
|
||||
/// </summary>
|
||||
/// <value>The navigation manager.</value>
|
||||
NavigationManager? navigationManager { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ContextMenuService"/> class.
|
||||
/// </summary>
|
||||
/// <param name="uriHelper">The URI helper.</param>
|
||||
public ContextMenuService(NavigationManager? uriHelper)
|
||||
{
|
||||
this.OnNavigate?.Invoke();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when [on navigate].
|
||||
/// </summary>
|
||||
public event Action OnNavigate;
|
||||
|
||||
/// <summary>
|
||||
/// Raises the Close event.
|
||||
/// </summary>
|
||||
public event Action OnClose;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when [on open].
|
||||
/// </summary>
|
||||
public event Action<MouseEventArgs, ContextMenuOptions> OnOpen;
|
||||
|
||||
/// <summary>
|
||||
/// Opens the specified arguments.
|
||||
/// </summary>
|
||||
/// <param name="args">The <see cref="MouseEventArgs"/> instance containing the event data.</param>
|
||||
/// <param name="items">The items.</param>
|
||||
/// <param name="click">The click.</param>
|
||||
public void Open(MouseEventArgs args, IEnumerable<ContextMenuItem> items, Action<MenuItemEventArgs> click = null)
|
||||
{
|
||||
var options = new ContextMenuOptions();
|
||||
|
||||
options.Items = items;
|
||||
options.Click = click;
|
||||
|
||||
OpenTooltip(args, options);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens the specified arguments.
|
||||
/// </summary>
|
||||
/// <param name="args">The <see cref="MouseEventArgs"/> instance containing the event data.</param>
|
||||
/// <param name="childContent">Content of the child.</param>
|
||||
public void Open(MouseEventArgs args, RenderFragment<ContextMenuService> childContent)
|
||||
{
|
||||
var options = new ContextMenuOptions();
|
||||
|
||||
options.ChildContent = childContent;
|
||||
|
||||
OpenTooltip(args, options);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens the tooltip.
|
||||
/// </summary>
|
||||
/// <param name="args">The <see cref="MouseEventArgs"/> instance containing the event data.</param>
|
||||
/// <param name="options">The options.</param>
|
||||
private void OpenTooltip(MouseEventArgs args, ContextMenuOptions options)
|
||||
{
|
||||
OnOpen?.Invoke(args, options);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes this instance.
|
||||
/// </summary>
|
||||
public void Close()
|
||||
{
|
||||
OnClose?.Invoke();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes this instance.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
navigationManager.LocationChanged -= UriHelper_OnLocationChanged;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class ContextMenuOptions.
|
||||
/// </summary>
|
||||
public class ContextMenuOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the child content.
|
||||
/// </summary>
|
||||
/// <value>The child content.</value>
|
||||
public RenderFragment<ContextMenuService> ChildContent { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the items.
|
||||
/// </summary>
|
||||
/// <value>The items.</value>
|
||||
public IEnumerable<ContextMenuItem> Items { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the click.
|
||||
/// </summary>
|
||||
/// <value>The click.</value>
|
||||
public Action<MenuItemEventArgs> Click { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class ContextMenu.
|
||||
/// </summary>
|
||||
public class ContextMenu
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the options.
|
||||
/// </summary>
|
||||
/// <value>The options.</value>
|
||||
public ContextMenuOptions Options { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the mouse event arguments.
|
||||
/// </summary>
|
||||
/// <value>The mouse event arguments.</value>
|
||||
public MouseEventArgs MouseEventArgs { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class ContextMenuItem.
|
||||
/// </summary>
|
||||
public class ContextMenuItem
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the text.
|
||||
/// </summary>
|
||||
/// <value>The text.</value>
|
||||
public string Text { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the value.
|
||||
/// </summary>
|
||||
/// <value>The value.</value>
|
||||
public object Value { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the icon.
|
||||
/// </summary>
|
||||
/// <value>The icon.</value>
|
||||
public string Icon { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the icon color.
|
||||
/// </summary>
|
||||
/// <value>The icon color.</value>
|
||||
public string IconColor { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the image.
|
||||
/// </summary>
|
||||
/// <value>The image.</value>
|
||||
public string Image { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the image style.
|
||||
/// </summary>
|
||||
/// <value>The image style.</value>
|
||||
public string ImageStyle { get; set; }
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this instance is disabled.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if this instance is disabled; otherwise, <c>false</c>.</value>
|
||||
public bool Disabled { get; set; }
|
||||
}
|
||||
}
|
||||
navigationManager = uriHelper;
|
||||
|
||||
if (navigationManager != null)
|
||||
{
|
||||
navigationManager.LocationChanged += UriHelper_OnLocationChanged;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles the OnLocationChanged event of the UriHelper control.
|
||||
/// </summary>
|
||||
/// <param name="sender">The source of the event.</param>
|
||||
/// <param name="e">The <see cref="Microsoft.AspNetCore.Components.Routing.LocationChangedEventArgs"/> instance containing the event data.</param>
|
||||
private void UriHelper_OnLocationChanged(object? sender, Microsoft.AspNetCore.Components.Routing.LocationChangedEventArgs e)
|
||||
{
|
||||
this.OnNavigate?.Invoke();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when [on navigate].
|
||||
/// </summary>
|
||||
public event Action? OnNavigate;
|
||||
|
||||
/// <summary>
|
||||
/// Raises the Close event.
|
||||
/// </summary>
|
||||
public event Action? OnClose;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when [on open].
|
||||
/// </summary>
|
||||
public event Action<MouseEventArgs, ContextMenuOptions>? OnOpen;
|
||||
|
||||
/// <summary>
|
||||
/// Opens the specified arguments.
|
||||
/// </summary>
|
||||
/// <param name="args">The <see cref="MouseEventArgs"/> instance containing the event data.</param>
|
||||
/// <param name="items">The items.</param>
|
||||
/// <param name="click">The click.</param>
|
||||
public void Open(MouseEventArgs args, IEnumerable<ContextMenuItem> items, Action<MenuItemEventArgs>? click = null)
|
||||
{
|
||||
var options = new ContextMenuOptions();
|
||||
|
||||
options.Items = items;
|
||||
options.Click = click;
|
||||
|
||||
OpenTooltip(args, options);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens the specified arguments.
|
||||
/// </summary>
|
||||
/// <param name="args">The <see cref="MouseEventArgs"/> instance containing the event data.</param>
|
||||
/// <param name="childContent">Content of the child.</param>
|
||||
public void Open(MouseEventArgs args, RenderFragment<ContextMenuService> childContent)
|
||||
{
|
||||
var options = new ContextMenuOptions();
|
||||
|
||||
options.ChildContent = childContent;
|
||||
|
||||
OpenTooltip(args, options);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens the tooltip.
|
||||
/// </summary>
|
||||
/// <param name="args">The <see cref="MouseEventArgs"/> instance containing the event data.</param>
|
||||
/// <param name="options">The options.</param>
|
||||
private void OpenTooltip(MouseEventArgs args, ContextMenuOptions options)
|
||||
{
|
||||
OnOpen?.Invoke(args, options);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes this instance.
|
||||
/// </summary>
|
||||
public void Close()
|
||||
{
|
||||
OnClose?.Invoke();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes this instance.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (navigationManager != null)
|
||||
{
|
||||
navigationManager.LocationChanged -= UriHelper_OnLocationChanged;
|
||||
}
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class ContextMenuOptions.
|
||||
/// </summary>
|
||||
public class ContextMenuOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the child content.
|
||||
/// </summary>
|
||||
/// <value>The child content.</value>
|
||||
public RenderFragment<ContextMenuService>? ChildContent { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the items.
|
||||
/// </summary>
|
||||
/// <value>The items.</value>
|
||||
public IEnumerable<ContextMenuItem>? Items { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the click.
|
||||
/// </summary>
|
||||
/// <value>The click.</value>
|
||||
public Action<MenuItemEventArgs>? Click { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class ContextMenu.
|
||||
/// </summary>
|
||||
public class ContextMenu
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the options.
|
||||
/// </summary>
|
||||
/// <value>The options.</value>
|
||||
public ContextMenuOptions? Options { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the mouse event arguments.
|
||||
/// </summary>
|
||||
/// <value>The mouse event arguments.</value>
|
||||
public MouseEventArgs? MouseEventArgs { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class ContextMenuItem.
|
||||
/// </summary>
|
||||
public class ContextMenuItem
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the text.
|
||||
/// </summary>
|
||||
/// <value>The text.</value>
|
||||
public string? Text { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the value.
|
||||
/// </summary>
|
||||
/// <value>The value.</value>
|
||||
public object? Value { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the icon.
|
||||
/// </summary>
|
||||
/// <value>The icon.</value>
|
||||
public string? Icon { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the icon color.
|
||||
/// </summary>
|
||||
/// <value>The icon color.</value>
|
||||
public string? IconColor { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the image.
|
||||
/// </summary>
|
||||
/// <value>The image.</value>
|
||||
public string? Image { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the image style.
|
||||
/// </summary>
|
||||
/// <value>The image style.</value>
|
||||
public string? ImageStyle { get; set; }
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this instance is disabled.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if this instance is disabled; otherwise, <c>false</c>.</value>
|
||||
public bool Disabled { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
92
Radzen.Blazor/ConversationSession.cs
Normal file
92
Radzen.Blazor/ConversationSession.cs
Normal file
@@ -0,0 +1,92 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Radzen.Blazor;
|
||||
|
||||
namespace Radzen;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a conversation session with memory.
|
||||
/// </summary>
|
||||
public class ConversationSession
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the unique identifier for the conversation session.
|
||||
/// </summary>
|
||||
public string Id { get; set; } = Guid.NewGuid().ToString();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the list of messages in the conversation.
|
||||
/// </summary>
|
||||
public List<ChatMessage> Messages { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the timestamp when the conversation was created.
|
||||
/// </summary>
|
||||
public DateTime CreatedAt { get; set; } = DateTime.Now;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the timestamp when the conversation was last updated.
|
||||
/// </summary>
|
||||
public DateTime LastUpdated { get; set; } = DateTime.Now;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum number of messages to keep in memory.
|
||||
/// </summary>
|
||||
public int MaxMessages { get; set; } = 50;
|
||||
|
||||
/// <summary>
|
||||
/// Adds a message to the conversation and manages memory limits.
|
||||
/// </summary>
|
||||
/// <param name="role">The role of the message sender.</param>
|
||||
/// <param name="content">The message content.</param>
|
||||
public void AddMessage(string role, string content)
|
||||
{
|
||||
Messages.Add(new ChatMessage
|
||||
{
|
||||
UserId = role,
|
||||
Role = role,
|
||||
IsUser = role == "user",
|
||||
Content = content,
|
||||
Timestamp = DateTime.Now
|
||||
});
|
||||
|
||||
LastUpdated = DateTime.Now;
|
||||
|
||||
// Remove oldest messages if we exceed the limit
|
||||
while (Messages.Count > MaxMessages)
|
||||
{
|
||||
Messages.RemoveAt(0);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all messages from the conversation.
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
Messages.Clear();
|
||||
LastUpdated = DateTime.Now;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the conversation messages formatted for the AI API.
|
||||
/// </summary>
|
||||
/// <param name="systemPrompt">The system prompt to include.</param>
|
||||
/// <returns>A list of message objects for the AI API.</returns>
|
||||
public List<object> GetFormattedMessages(string systemPrompt)
|
||||
{
|
||||
var messages = new List<object>();
|
||||
|
||||
// Add system message
|
||||
messages.Add(new { role = "system", content = systemPrompt });
|
||||
|
||||
// Add conversation messages
|
||||
foreach (var message in Messages)
|
||||
{
|
||||
messages.Add(new { role = message.Role, content = message.Content });
|
||||
}
|
||||
|
||||
return messages;
|
||||
}
|
||||
}
|
||||
|
||||
70
Radzen.Blazor/ConvertType.cs
Normal file
70
Radzen.Blazor/ConvertType.cs
Normal file
@@ -0,0 +1,70 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
|
||||
namespace Radzen;
|
||||
|
||||
/// <summary>
|
||||
/// Converts values to different types. Used internally.
|
||||
/// </summary>
|
||||
public static class ConvertType
|
||||
{
|
||||
/// <summary>
|
||||
/// Changes the type of an object.
|
||||
/// </summary>
|
||||
/// <param name="value">The value.</param>
|
||||
/// <param name="type">The type.</param>
|
||||
/// <param name="culture">The culture.</param>
|
||||
/// <returns>System.Object</returns>
|
||||
public static object? ChangeType(object value, Type type, CultureInfo? culture = null)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(type);
|
||||
|
||||
// CA1062: Validate 'value' is non-null before using it
|
||||
if (value == null)
|
||||
{
|
||||
if (Nullable.GetUnderlyingType(type) != null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
}
|
||||
|
||||
if (culture == null)
|
||||
{
|
||||
culture = CultureInfo.CurrentCulture;
|
||||
}
|
||||
|
||||
if ((Nullable.GetUnderlyingType(type) ?? type) == typeof(Guid) && value is string)
|
||||
{
|
||||
return Guid.Parse((string)value);
|
||||
}
|
||||
|
||||
var underlyingEnumType = Nullable.GetUnderlyingType(type);
|
||||
if (underlyingEnumType?.IsEnum == true)
|
||||
{
|
||||
var valueString = value.ToString();
|
||||
if (valueString == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(value), "Enum value cannot be null.");
|
||||
}
|
||||
return Enum.Parse(underlyingEnumType, valueString);
|
||||
}
|
||||
|
||||
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IEnumerable<>))
|
||||
{
|
||||
Type itemType = type.GetGenericArguments()[0];
|
||||
var enumerable = value as IEnumerable<object>;
|
||||
|
||||
if (enumerable != null)
|
||||
{
|
||||
return enumerable.AsQueryable().Cast(itemType);
|
||||
}
|
||||
}
|
||||
|
||||
return value is IConvertible ? Convert.ChangeType(value, Nullable.GetUnderlyingType(type) ?? type, culture) : value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,12 +44,12 @@ namespace Radzen
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to use secure cookies.
|
||||
/// </summary>
|
||||
public bool IsSecure { get; set; } = false;
|
||||
public bool IsSecure { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the SameSite attribute for the cookie.
|
||||
/// </summary>
|
||||
public CookieSameSiteMode? SameSite { get; set; } = null;
|
||||
public CookieSameSiteMode? SameSite { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -64,13 +64,16 @@ namespace Radzen
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CookieThemeService" /> class.
|
||||
/// </summary>
|
||||
public CookieThemeService(IJSRuntime jsRuntime, ThemeService themeService, IOptions<CookieThemeServiceOptions> options)
|
||||
public CookieThemeService(IJSRuntime jsRuntime, ThemeService themeService, IOptions<CookieThemeServiceOptions>? options)
|
||||
{
|
||||
this.jsRuntime = jsRuntime;
|
||||
this.themeService = themeService;
|
||||
this.options = options.Value;
|
||||
this.options = options?.Value ?? new CookieThemeServiceOptions();
|
||||
|
||||
themeService.ThemeChanged += OnThemeChanged;
|
||||
if (themeService != null)
|
||||
{
|
||||
themeService.ThemeChanged += OnThemeChanged;
|
||||
}
|
||||
|
||||
_ = InitializeAsync();
|
||||
}
|
||||
|
||||
18
Radzen.Blazor/CoordinateSystem.cs
Normal file
18
Radzen.Blazor/CoordinateSystem.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
namespace Radzen;
|
||||
|
||||
/// <summary>
|
||||
/// CoordinateSystem enum
|
||||
/// </summary>
|
||||
public enum CoordinateSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// Cartesian CoordinateSystem
|
||||
/// </summary>
|
||||
Cartesian,
|
||||
|
||||
/// <summary>
|
||||
/// Polar CoordinateSystem
|
||||
/// </summary>
|
||||
Polar
|
||||
}
|
||||
|
||||
@@ -47,14 +47,14 @@ namespace Radzen
|
||||
/// </summary>
|
||||
/// <value>The name.</value>
|
||||
[Parameter]
|
||||
public string Name { get; set; }
|
||||
public string? Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the placeholder.
|
||||
/// </summary>
|
||||
/// <value>The placeholder.</value>
|
||||
[Parameter]
|
||||
public string Placeholder { get; set; }
|
||||
public string? Placeholder { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this <see cref="DataBoundFormComponent{T}"/> is disabled.
|
||||
@@ -80,36 +80,36 @@ namespace Radzen
|
||||
/// <summary>
|
||||
/// The form
|
||||
/// </summary>
|
||||
IRadzenForm _form;
|
||||
IRadzenForm? form;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the form.
|
||||
/// </summary>
|
||||
/// <value>The form.</value>
|
||||
[CascadingParameter]
|
||||
public IRadzenForm Form
|
||||
public IRadzenForm? Form
|
||||
{
|
||||
get
|
||||
{
|
||||
return _form;
|
||||
return form;
|
||||
}
|
||||
set
|
||||
{
|
||||
_form = value;
|
||||
_form?.AddComponent(this);
|
||||
form = value;
|
||||
form?.AddComponent(this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The value
|
||||
/// </summary>
|
||||
private T _value = default;
|
||||
private T? _value;
|
||||
/// <summary>
|
||||
/// Gets or sets the value.
|
||||
/// </summary>
|
||||
/// <value>The value.</value>
|
||||
[Parameter]
|
||||
public T Value
|
||||
public T? Value
|
||||
{
|
||||
get
|
||||
{
|
||||
@@ -167,18 +167,18 @@ namespace Radzen
|
||||
/// </summary>
|
||||
/// <value>The text property.</value>
|
||||
[Parameter]
|
||||
public string TextProperty { get; set; }
|
||||
public string? TextProperty { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The data
|
||||
/// </summary>
|
||||
IEnumerable _data = null;
|
||||
IEnumerable? _data;
|
||||
/// <summary>
|
||||
/// Gets or sets the data.
|
||||
/// </summary>
|
||||
/// <value>The data.</value>
|
||||
[Parameter]
|
||||
public virtual IEnumerable Data
|
||||
public virtual IEnumerable? Data
|
||||
{
|
||||
get
|
||||
{
|
||||
@@ -208,7 +208,7 @@ namespace Radzen
|
||||
/// Gets the query.
|
||||
/// </summary>
|
||||
/// <value>The query.</value>
|
||||
protected virtual IQueryable Query
|
||||
protected virtual IQueryable? Query
|
||||
{
|
||||
get
|
||||
{
|
||||
@@ -220,7 +220,7 @@ namespace Radzen
|
||||
/// Gets or sets the search text
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public string SearchText
|
||||
public string? SearchText
|
||||
{
|
||||
get
|
||||
{
|
||||
@@ -245,29 +245,30 @@ namespace Radzen
|
||||
/// <summary>
|
||||
/// The search text
|
||||
/// </summary>
|
||||
internal string searchText;
|
||||
internal string? searchText;
|
||||
|
||||
/// <summary>
|
||||
/// The view
|
||||
/// </summary>
|
||||
protected IQueryable _view = null;
|
||||
protected IQueryable? _view;
|
||||
/// <summary>
|
||||
/// Gets the view.
|
||||
/// </summary>
|
||||
/// <value>The view.</value>
|
||||
protected virtual IEnumerable View
|
||||
protected virtual IEnumerable? View
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_view == null && Query != null)
|
||||
var query = Query;
|
||||
if (_view == null && query != null)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(searchText))
|
||||
if (!string.IsNullOrEmpty(searchText) && !string.IsNullOrEmpty(TextProperty))
|
||||
{
|
||||
_view = Query.Where(TextProperty, searchText, FilterOperator, FilterCaseSensitivity);
|
||||
_view = query.Where(TextProperty, searchText, FilterOperator, FilterCaseSensitivity);
|
||||
}
|
||||
else
|
||||
{
|
||||
_view = (typeof(IQueryable).IsAssignableFrom(Data.GetType())) ? Query.Cast<object>().ToList().AsQueryable() : Query;
|
||||
_view = Data is IQueryable ? query.Cast<object>().ToList().AsQueryable() : query;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -275,12 +276,14 @@ namespace Radzen
|
||||
}
|
||||
}
|
||||
|
||||
internal IEnumerable? GetView() => View;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the edit context.
|
||||
/// </summary>
|
||||
/// <value>The edit context.</value>
|
||||
[CascadingParameter]
|
||||
public EditContext EditContext { get; set; }
|
||||
public EditContext? EditContext { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the field identifier.
|
||||
@@ -294,7 +297,7 @@ namespace Radzen
|
||||
/// </summary>
|
||||
/// <value>The value expression.</value>
|
||||
[Parameter]
|
||||
public Expression<Func<T>> ValueExpression { get; set; }
|
||||
public Expression<Func<T>>? ValueExpression { get; set; }
|
||||
/// <summary>
|
||||
/// Set parameters as an asynchronous operation.
|
||||
/// </summary>
|
||||
@@ -305,7 +308,7 @@ namespace Radzen
|
||||
// check for changes before setting the properties through the base call
|
||||
var dataChanged = parameters.DidParameterChange(nameof(Data), Data);
|
||||
var disabledChanged = parameters.DidParameterChange(nameof(Disabled), Disabled);
|
||||
|
||||
|
||||
// allow the base class to process parameters and set the properties
|
||||
// after this call the parameters object should be considered stale
|
||||
await base.SetParametersAsync(parameters);
|
||||
@@ -316,9 +319,11 @@ namespace Radzen
|
||||
await OnDataChanged();
|
||||
}
|
||||
|
||||
if (EditContext != null && ValueExpression != null && FieldIdentifier.Model != EditContext.Model)
|
||||
if (EditContext != null && (ValueExpression != null || ValueChanged.HasDelegate) && FieldIdentifier.Model != EditContext.Model)
|
||||
{
|
||||
FieldIdentifier = FieldIdentifier.Create(ValueExpression);
|
||||
FieldIdentifier = ValueExpression != null
|
||||
? FieldIdentifier.Create(ValueExpression)
|
||||
: FieldIdentifier.Create(() => Value);
|
||||
EditContext.OnValidationStateChanged -= ValidationStateChanged;
|
||||
EditContext.OnValidationStateChanged += ValidationStateChanged;
|
||||
}
|
||||
@@ -334,7 +339,7 @@ namespace Radzen
|
||||
/// </summary>
|
||||
/// <param name="sender">The sender.</param>
|
||||
/// <param name="e">The <see cref="ValidationStateChangedEventArgs"/> instance containing the event data.</param>
|
||||
private void ValidationStateChanged(object sender, ValidationStateChangedEventArgs e)
|
||||
private void ValidationStateChanged(object? sender, ValidationStateChangedEventArgs e)
|
||||
{
|
||||
StateHasChanged();
|
||||
}
|
||||
@@ -352,13 +357,15 @@ namespace Radzen
|
||||
}
|
||||
|
||||
Form?.RemoveComponent(this);
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value.
|
||||
/// </summary>
|
||||
/// <returns>System.Object.</returns>
|
||||
public virtual object GetValue()
|
||||
public virtual object? GetValue()
|
||||
{
|
||||
return Value;
|
||||
}
|
||||
@@ -382,13 +389,13 @@ namespace Radzen
|
||||
|
||||
/// <summary> Provides support for RadzenFormField integration. </summary>
|
||||
[CascadingParameter]
|
||||
public IFormFieldContext FormFieldContext { get; set; }
|
||||
public IFormFieldContext? FormFieldContext { get; set; }
|
||||
|
||||
/// <summary> Gets the current placeholder. Returns empty string if this component is inside a RadzenFormField.</summary>
|
||||
protected string CurrentPlaceholder => FormFieldContext?.AllowFloatingLabel == true ? " " : Placeholder;
|
||||
protected string? CurrentPlaceholder => FormFieldContext?.AllowFloatingLabel == true ? " " : Placeholder;
|
||||
|
||||
/// <summary>
|
||||
/// Handles the <see cref="E:ContextMenu" /> event.
|
||||
/// Handles the ContextMenu event.
|
||||
/// </summary>
|
||||
/// <param name="args">The <see cref="MouseEventArgs"/> instance containing the event data.</param>
|
||||
/// <returns>Task.</returns>
|
||||
|
||||
21
Radzen.Blazor/DataGridCellMouseEventArgs.cs
Normal file
21
Radzen.Blazor/DataGridCellMouseEventArgs.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using Radzen.Blazor;
|
||||
|
||||
namespace Radzen;
|
||||
|
||||
/// <summary>
|
||||
/// Supplies information about a <see cref="RadzenDataGrid{TItem}.CellContextMenu" /> event that is being raised.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The data item type.</typeparam>
|
||||
public class DataGridCellMouseEventArgs<T> : Microsoft.AspNetCore.Components.Web.MouseEventArgs where T : notnull
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the data item which the clicked DataGrid row represents.
|
||||
/// </summary>
|
||||
public T? Data { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the RadzenDataGridColumn which this cells represents.
|
||||
/// </summary>
|
||||
public RadzenDataGridColumn<T>? Column { get; internal set; }
|
||||
}
|
||||
|
||||
16
Radzen.Blazor/DataGridCellRenderEventArgs.cs
Normal file
16
Radzen.Blazor/DataGridCellRenderEventArgs.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using Radzen.Blazor;
|
||||
|
||||
namespace Radzen;
|
||||
|
||||
/// <summary>
|
||||
/// Supplies information about a <see cref="RadzenDataGrid{TItem}.CellRender" /> event that is being raised.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The data item type.</typeparam>
|
||||
public class DataGridCellRenderEventArgs<T> : RowRenderEventArgs<T> where T : notnull
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the RadzenDataGridColumn which this cells represents.
|
||||
/// </summary>
|
||||
public RadzenDataGridColumn<T>? Column { get; internal set; }
|
||||
}
|
||||
|
||||
26
Radzen.Blazor/DataGridChildData.cs
Normal file
26
Radzen.Blazor/DataGridChildData.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Radzen;
|
||||
|
||||
/// <summary>
|
||||
/// Internal class for managing hierarchical child data in DataGrid.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The data item type.</typeparam>
|
||||
internal class DataGridChildData<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the parent child data.
|
||||
/// </summary>
|
||||
internal DataGridChildData<T>? ParentChildData { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the level.
|
||||
/// </summary>
|
||||
internal int Level { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the data.
|
||||
/// </summary>
|
||||
internal IEnumerable<T>? Data { get; set; }
|
||||
}
|
||||
|
||||
41
Radzen.Blazor/DataGridColumnFilterEventArgs.cs
Normal file
41
Radzen.Blazor/DataGridColumnFilterEventArgs.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
using Radzen.Blazor;
|
||||
|
||||
namespace Radzen;
|
||||
|
||||
/// <summary>
|
||||
/// Supplies information about a <see cref="RadzenDataGrid{TItem}.Filter" /> event that is being raised.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The data item type.</typeparam>
|
||||
public class DataGridColumnFilterEventArgs<T> where T : notnull
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the filtered RadzenDataGridColumn.
|
||||
/// </summary>
|
||||
public RadzenDataGridColumn<T>? Column { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the new filter value of the filtered column.
|
||||
/// </summary>
|
||||
public object? FilterValue { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the new second filter value of the filtered column.
|
||||
/// </summary>
|
||||
public object? SecondFilterValue { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the new filter operator of the filtered column.
|
||||
/// </summary>
|
||||
public FilterOperator FilterOperator { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the new second filter operator of the filtered column.
|
||||
/// </summary>
|
||||
public FilterOperator SecondFilterOperator { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the new logical filter operator of the filtered column.
|
||||
/// </summary>
|
||||
public LogicalFilterOperator LogicalFilterOperator { get; internal set; }
|
||||
}
|
||||
|
||||
21
Radzen.Blazor/DataGridColumnGroupEventArgs.cs
Normal file
21
Radzen.Blazor/DataGridColumnGroupEventArgs.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using Radzen.Blazor;
|
||||
|
||||
namespace Radzen;
|
||||
|
||||
/// <summary>
|
||||
/// Supplies information about a <see cref="RadzenDataGrid{TItem}.Group" /> event that is being raised.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The data item type.</typeparam>
|
||||
public class DataGridColumnGroupEventArgs<T> where T : notnull
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the grouped RadzenDataGridColumn.
|
||||
/// </summary>
|
||||
public RadzenDataGridColumn<T>? Column { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the group descriptor.
|
||||
/// </summary>
|
||||
public GroupDescriptor? GroupDescriptor { get; internal set; }
|
||||
}
|
||||
|
||||
26
Radzen.Blazor/DataGridColumnReorderedEventArgs.cs
Normal file
26
Radzen.Blazor/DataGridColumnReorderedEventArgs.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using Radzen.Blazor;
|
||||
|
||||
namespace Radzen;
|
||||
|
||||
/// <summary>
|
||||
/// Supplies information about a <see cref="RadzenDataGrid{TItem}.ColumnReordered" /> event that is being raised.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The data item type.</typeparam>
|
||||
public class DataGridColumnReorderedEventArgs<T> where T : notnull
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the reordered RadzenDataGridColumn.
|
||||
/// </summary>
|
||||
public RadzenDataGridColumn<T>? Column { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the old index of the column.
|
||||
/// </summary>
|
||||
public int OldIndex { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the new index of the column.
|
||||
/// </summary>
|
||||
public int NewIndex { get; internal set; }
|
||||
}
|
||||
|
||||
27
Radzen.Blazor/DataGridColumnReorderingEventArgs.cs
Normal file
27
Radzen.Blazor/DataGridColumnReorderingEventArgs.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using Radzen.Blazor;
|
||||
|
||||
namespace Radzen;
|
||||
|
||||
/// <summary>
|
||||
/// Supplies information about a <see cref="RadzenDataGrid{TItem}.ColumnReordering" /> event that is being raised.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The data item type.</typeparam>
|
||||
public class DataGridColumnReorderingEventArgs<T> where T : notnull
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the reordered RadzenDataGridColumn.
|
||||
/// </summary>
|
||||
public RadzenDataGridColumn<T>? Column { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the reordered to RadzenDataGridColumn.
|
||||
/// </summary>
|
||||
public RadzenDataGridColumn<T>? ToColumn { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value which will cancel the event.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> to cancel the event; otherwise, <c>false</c>.</value>
|
||||
public bool Cancel { get; set; }
|
||||
}
|
||||
|
||||
21
Radzen.Blazor/DataGridColumnResizedEventArgs.cs
Normal file
21
Radzen.Blazor/DataGridColumnResizedEventArgs.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using Radzen.Blazor;
|
||||
|
||||
namespace Radzen;
|
||||
|
||||
/// <summary>
|
||||
/// Supplies information about a <see cref="RadzenDataGrid{TItem}.ColumnResized" /> event that is being raised.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The data item type.</typeparam>
|
||||
public class DataGridColumnResizedEventArgs<T> where T : notnull
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the resized RadzenDataGridColumn.
|
||||
/// </summary>
|
||||
public RadzenDataGridColumn<T>? Column { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the new width of the resized column.
|
||||
/// </summary>
|
||||
public double Width { get; internal set; }
|
||||
}
|
||||
|
||||
81
Radzen.Blazor/DataGridColumnSettings.cs
Normal file
81
Radzen.Blazor/DataGridColumnSettings.cs
Normal file
@@ -0,0 +1,81 @@
|
||||
namespace Radzen;
|
||||
|
||||
/// <summary>
|
||||
/// DataGrid column settings class used to Save/Load settings.
|
||||
/// </summary>
|
||||
public class DataGridColumnSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// Property.
|
||||
/// </summary>
|
||||
public string UniqueID { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Property.
|
||||
/// </summary>
|
||||
public string Property { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Visible.
|
||||
/// </summary>
|
||||
public bool Visible { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Width.
|
||||
/// </summary>
|
||||
public string Width { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// OrderIndex.
|
||||
/// </summary>
|
||||
public int? OrderIndex { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// SortOrder.
|
||||
/// </summary>
|
||||
public SortOrder? SortOrder { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// SortIndex.
|
||||
/// </summary>
|
||||
public int? SortIndex { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// FilterValue.
|
||||
/// </summary>
|
||||
public object? FilterValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// FilterOperator.
|
||||
/// </summary>
|
||||
public FilterOperator FilterOperator { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// SecondFilterValue.
|
||||
/// </summary>
|
||||
public object? SecondFilterValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// SecondFilterOperator.
|
||||
/// </summary>
|
||||
public FilterOperator SecondFilterOperator { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// LogicalFilterOperator.
|
||||
/// </summary>
|
||||
public LogicalFilterOperator LogicalFilterOperator { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// CustomFilterExpression.
|
||||
/// </summary>
|
||||
public string CustomFilterExpression { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the mode that determines whether the filter applies to any or all items in a collection.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="CollectionFilterMode"/> value indicating whether the filter is satisfied by any or all items.
|
||||
/// </value>
|
||||
public CollectionFilterMode CollectionFilterMode { get; set; }
|
||||
}
|
||||
|
||||
21
Radzen.Blazor/DataGridColumnSortEventArgs.cs
Normal file
21
Radzen.Blazor/DataGridColumnSortEventArgs.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using Radzen.Blazor;
|
||||
|
||||
namespace Radzen;
|
||||
|
||||
/// <summary>
|
||||
/// Supplies information about a <see cref="RadzenDataGrid{TItem}.Sort" /> event that is being raised.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The data item type.</typeparam>
|
||||
public class DataGridColumnSortEventArgs<T> where T : notnull
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the sorted RadzenDataGridColumn.
|
||||
/// </summary>
|
||||
public RadzenDataGridColumn<T>? Column { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the new sort order of the sorted column.
|
||||
/// </summary>
|
||||
public SortOrder? SortOrder { get; internal set; }
|
||||
}
|
||||
|
||||
18
Radzen.Blazor/DataGridEditMode.cs
Normal file
18
Radzen.Blazor/DataGridEditMode.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
namespace Radzen;
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the inline edit mode behavior of <see cref="Radzen.Blazor.RadzenDataGrid{TItem}" />.
|
||||
/// </summary>
|
||||
public enum DataGridEditMode
|
||||
{
|
||||
/// <summary>
|
||||
/// The user can edit only one row at a time. Editing a different row cancels edit mode for the last edited row.
|
||||
/// </summary>
|
||||
Single,
|
||||
|
||||
/// <summary>
|
||||
/// The user can edit multiple rows.
|
||||
/// </summary>
|
||||
Multiple
|
||||
}
|
||||
|
||||
18
Radzen.Blazor/DataGridExpandMode.cs
Normal file
18
Radzen.Blazor/DataGridExpandMode.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
namespace Radzen;
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the expand behavior of <see cref="Radzen.Blazor.RadzenDataGrid{TItem}" />.
|
||||
/// </summary>
|
||||
public enum DataGridExpandMode
|
||||
{
|
||||
/// <summary>
|
||||
/// The user can expand only one row at a time. Expanding a different row collapses the last expanded row.
|
||||
/// </summary>
|
||||
Single,
|
||||
|
||||
/// <summary>
|
||||
/// The user can expand multiple rows.
|
||||
/// </summary>
|
||||
Multiple
|
||||
}
|
||||
|
||||
33
Radzen.Blazor/DataGridGridLines.cs
Normal file
33
Radzen.Blazor/DataGridGridLines.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
namespace Radzen;
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the grid lines of <see cref="Radzen.Blazor.RadzenDataGrid{TItem}" />.
|
||||
/// </summary>
|
||||
public enum DataGridGridLines
|
||||
{
|
||||
/// <summary>
|
||||
/// Theme default.
|
||||
/// </summary>
|
||||
Default,
|
||||
|
||||
/// <summary>
|
||||
/// Both horizontal and vertical grid lines.
|
||||
/// </summary>
|
||||
Both,
|
||||
|
||||
/// <summary>
|
||||
/// No grid lines.
|
||||
/// </summary>
|
||||
None,
|
||||
|
||||
/// <summary>
|
||||
/// Horizontal grid lines.
|
||||
/// </summary>
|
||||
Horizontal,
|
||||
|
||||
/// <summary>
|
||||
/// Vertical grid lines.
|
||||
/// </summary>
|
||||
Vertical
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user