├── .gitattributes ├── .gitignore ├── BlazorPro.BlazorSize.BlazorRepl ├── BlazorPro.BlazorSize.BlazorRepl.csproj ├── BlazorReplMediaQueryService.cs └── LICENSE.txt ├── BlazorPro.BlazorSize.Bunit ├── BlazorPro.BlazorSize.Bunit.csproj ├── BunitExtensions.cs └── FakeMediaQueryService.cs ├── BlazorSize.UnitTesting ├── BlazorPro.BlazorSize.UnitTesting.csproj ├── Bunit │ ├── BunitExtensions.cs │ └── FakeMediaQueryService.cs └── LICENSE.txt ├── BlazorSize.e2eTests ├── BlazorSize Adaptive Table and Template.imgstore ├── BlazorSize Adaptive Table and Template.resx ├── BlazorSize Adaptive Table and Template.tstest ├── BlazorSize Adaptive Table and Template.tstest.cs ├── BlazorSize Construct & Dispose.imgstore ├── BlazorSize Construct & Dispose.resx ├── BlazorSize Construct & Dispose.tstest ├── BlazorSize Size Reverse.imgstore ├── BlazorSize Size Reverse.resx ├── BlazorSize Size Reverse.tstest ├── BlazorSize Size Reverse.tstest.cs ├── BlazorSize Size Tests.imgstore ├── BlazorSize Size Tests.resx ├── BlazorSize Size Tests.tstest ├── BlazorSize Size Tests.tstest.cs ├── BlazorSize Test Multicast Reverse.imgstore ├── BlazorSize Test Multicast Reverse.resx ├── BlazorSize Test Multicast Reverse.tstest ├── BlazorSize Test Multicast Reverse.tstest.cs ├── BlazorSize Test Multicast.imgstore ├── BlazorSize Test Multicast.resx ├── BlazorSize Test Multicast.tstest ├── BlazorSize Test Multicast.tstest.cs ├── BlazorSize.e2eTests.csproj ├── Profiler Configurations │ ├── {1c6f74bd-1a14-4014-a8c9-02fdb3cb8302}.tsprofconfig │ └── {d3bce0f5-b6f6-45aa-959a-5c1c1753902b}.tsprofconfig ├── Properties │ └── AssemblyInfo.cs ├── Settings.aiis └── TestLists │ └── BlazorSize .NET Web App.aiilist ├── BlazorSize.sln ├── BlazorSize ├── BlazorPro.BlazorSize.csproj ├── Configuration │ ├── ResizeOptions.cs │ └── ServiceCollectionExtensions.cs ├── LICENSE.txt ├── MediaQuery │ ├── Breakpoints.cs │ ├── IMediaQueryService.cs │ ├── MediaQuery.razor │ ├── MediaQuery.razor.cs │ ├── MediaQueryArgs.cs │ ├── MediaQueryCache.cs │ ├── MediaQueryList.razor │ ├── MediaQueryList.razor.cs │ └── MediaQueryService.cs ├── Resize │ ├── BrowserWindowSize.cs │ ├── IResizeListener.cs │ └── ResizeListener.cs ├── _Imports.razor ├── jest.config.js ├── package-lock.json ├── package.json ├── readme.md ├── scripts │ ├── blazorSizeMedia.ts │ ├── blazorSizeMediaModule.ts │ ├── blazorSizeResize.ts │ ├── blazorSizeResizeModule.ts │ └── tests │ │ └── blazorSize.test.ts ├── tsconfig.build.json └── tsconfig.json ├── BlazorWebApp ├── BlazorWebApp.Client │ ├── BlazorWebApp.Client.csproj │ ├── Layout │ │ ├── MainLayout.razor │ │ ├── MainLayout.razor.css │ │ ├── NavMenu.razor │ │ └── NavMenu.razor.css │ ├── Program.cs │ ├── Routes.razor │ ├── _Imports.razor │ └── wwwroot │ │ ├── appsettings.Development.json │ │ └── appsettings.json └── BlazorWebApp │ ├── BlazorWebApp.csproj │ ├── Components │ ├── App.razor │ ├── Pages │ │ └── Error.razor │ └── _Imports.razor │ ├── Program.cs │ ├── Properties │ └── launchSettings.json │ ├── appsettings.Development.json │ ├── appsettings.json │ └── wwwroot │ ├── app.css │ ├── bootstrap │ ├── bootstrap.min.css │ └── bootstrap.min.css.map │ └── favicon.png ├── PlaywrightTests ├── MediaQueryTests.cs ├── PlaywrightTests.csproj ├── ResizeTests.cs └── TestHelpers.cs ├── TestComponents ├── Data │ └── IWeatherForecastService.cs ├── Pages │ ├── Blank.razor │ ├── Error.razor │ ├── FetchData.razor │ ├── Index.razor │ ├── Multicast.razor │ ├── NullableFlag.razor │ ├── ScreenSize.razor │ ├── ScreenSizeReporter.razor │ └── Templates.razor ├── TestComponents.csproj ├── WeatherCards.razor ├── WeatherForecast.cs ├── WeatherGrid.razor ├── _Imports.razor └── wwwroot │ ├── fixture.html │ └── fixture.js ├── _resources └── blazor-glove-128.jpg ├── _sponsors └── Telerik │ ├── Blazor-300x250.png │ ├── blazor-300x250_v2.png │ ├── blazor-300x600.png │ └── blazor-300x600_v2.png ├── bUnitCompatibilityTests ├── CompTest.cs ├── Sample.razor ├── _Imports.razor └── bUnitCompatibilityTests.csproj ├── package-lock.json ├── package.json └── readme.md /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Build results 17 | [Dd]ebug/ 18 | [Dd]ebugPublic/ 19 | [Rr]elease/ 20 | [Rr]eleases/ 21 | x64/ 22 | x86/ 23 | [Aa][Rr][Mm]/ 24 | [Aa][Rr][Mm]64/ 25 | bld/ 26 | [Bb]in/ 27 | [Oo]bj/ 28 | [Ll]og/ 29 | 30 | # Visual Studio 2015/2017 cache/options directory 31 | .vs/ 32 | # Uncomment if you have tasks that create the project's static files in wwwroot 33 | #wwwroot/ 34 | 35 | # Visual Studio 2017 auto generated files 36 | Generated\ Files/ 37 | 38 | # MSTest test Results 39 | [Tt]est[Rr]esult*/ 40 | [Bb]uild[Ll]og.* 41 | 42 | # NUNIT 43 | *.VisualState.xml 44 | TestResult.xml 45 | 46 | # Build Results of an ATL Project 47 | [Dd]ebugPS/ 48 | [Rr]eleasePS/ 49 | dlldata.c 50 | 51 | # Benchmark Results 52 | BenchmarkDotNet.Artifacts/ 53 | 54 | # .NET Core 55 | project.lock.json 56 | project.fragment.lock.json 57 | artifacts/ 58 | 59 | # StyleCop 60 | StyleCopReport.xml 61 | 62 | # Files built by Visual Studio 63 | *_i.c 64 | *_p.c 65 | *_h.h 66 | *.ilk 67 | *.meta 68 | *.obj 69 | *.iobj 70 | *.pch 71 | *.pdb 72 | *.ipdb 73 | *.pgc 74 | *.pgd 75 | *.rsp 76 | *.sbr 77 | *.tlb 78 | *.tli 79 | *.tlh 80 | *.tmp 81 | *.tmp_proj 82 | *_wpftmp.csproj 83 | *.log 84 | *.vspscc 85 | *.vssscc 86 | .builds 87 | *.pidb 88 | *.svclog 89 | *.scc 90 | 91 | # Chutzpah Test files 92 | _Chutzpah* 93 | 94 | # Visual C++ cache files 95 | ipch/ 96 | *.aps 97 | *.ncb 98 | *.opendb 99 | *.opensdf 100 | *.sdf 101 | *.cachefile 102 | *.VC.db 103 | *.VC.VC.opendb 104 | 105 | # Visual Studio profiler 106 | *.psess 107 | *.vsp 108 | *.vspx 109 | *.sap 110 | 111 | # Visual Studio Trace Files 112 | *.e2e 113 | 114 | # TFS 2012 Local Workspace 115 | $tf/ 116 | 117 | # Guidance Automation Toolkit 118 | *.gpState 119 | 120 | # ReSharper is a .NET coding add-in 121 | _ReSharper*/ 122 | *.[Rr]e[Ss]harper 123 | *.DotSettings.user 124 | 125 | # JustCode is a .NET coding add-in 126 | .JustCode 127 | 128 | # TeamCity is a build add-in 129 | _TeamCity* 130 | 131 | # DotCover is a Code Coverage Tool 132 | *.dotCover 133 | 134 | # AxoCover is a Code Coverage Tool 135 | .axoCover/* 136 | !.axoCover/settings.json 137 | 138 | # Visual Studio code coverage results 139 | *.coverage 140 | *.coveragexml 141 | 142 | # NCrunch 143 | _NCrunch_* 144 | .*crunch*.local.xml 145 | nCrunchTemp_* 146 | 147 | # MightyMoose 148 | *.mm.* 149 | AutoTest.Net/ 150 | 151 | # Web workbench (sass) 152 | .sass-cache/ 153 | 154 | # Installshield output folder 155 | [Ee]xpress/ 156 | 157 | # DocProject is a documentation generator add-in 158 | DocProject/buildhelp/ 159 | DocProject/Help/*.HxT 160 | DocProject/Help/*.HxC 161 | DocProject/Help/*.hhc 162 | DocProject/Help/*.hhk 163 | DocProject/Help/*.hhp 164 | DocProject/Help/Html2 165 | DocProject/Help/html 166 | 167 | # Click-Once directory 168 | publish/ 169 | 170 | # Publish Web Output 171 | *.[Pp]ublish.xml 172 | *.azurePubxml 173 | # Note: Comment the next line if you want to checkin your web deploy settings, 174 | # but database connection strings (with potential passwords) will be unencrypted 175 | *.pubxml 176 | *.publishproj 177 | 178 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 179 | # checkin your Azure Web App publish settings, but sensitive information contained 180 | # in these scripts will be unencrypted 181 | PublishScripts/ 182 | 183 | # NuGet Packages 184 | *.nupkg 185 | # The packages folder can be ignored because of Package Restore 186 | **/[Pp]ackages/* 187 | # except build/, which is used as an MSBuild target. 188 | !**/[Pp]ackages/build/ 189 | # Uncomment if necessary however generally it will be regenerated when needed 190 | #!**/[Pp]ackages/repositories.config 191 | # NuGet v3's project.json files produces more ignorable files 192 | *.nuget.props 193 | *.nuget.targets 194 | 195 | # Microsoft Azure Build Output 196 | csx/ 197 | *.build.csdef 198 | 199 | # Microsoft Azure Emulator 200 | ecf/ 201 | rcf/ 202 | 203 | # Windows Store app package directories and files 204 | AppPackages/ 205 | BundleArtifacts/ 206 | Package.StoreAssociation.xml 207 | _pkginfo.txt 208 | *.appx 209 | 210 | # Visual Studio cache files 211 | # files ending in .cache can be ignored 212 | *.[Cc]ache 213 | # but keep track of directories ending in .cache 214 | !?*.[Cc]ache/ 215 | 216 | # Others 217 | ClientBin/ 218 | ~$* 219 | *~ 220 | *.dbmdl 221 | *.dbproj.schemaview 222 | *.jfm 223 | *.pfx 224 | *.publishsettings 225 | orleans.codegen.cs 226 | 227 | # Including strong name files can present a security risk 228 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 229 | #*.snk 230 | 231 | # Since there are multiple workflows, uncomment next line to ignore bower_components 232 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 233 | #bower_components/ 234 | 235 | # RIA/Silverlight projects 236 | Generated_Code/ 237 | 238 | # Backup & report files from converting an old project file 239 | # to a newer Visual Studio version. Backup files are not needed, 240 | # because we have git ;-) 241 | _UpgradeReport_Files/ 242 | Backup*/ 243 | UpgradeLog*.XML 244 | UpgradeLog*.htm 245 | ServiceFabricBackup/ 246 | *.rptproj.bak 247 | 248 | # SQL Server files 249 | *.mdf 250 | *.ldf 251 | *.ndf 252 | 253 | # Business Intelligence projects 254 | *.rdl.data 255 | *.bim.layout 256 | *.bim_*.settings 257 | *.rptproj.rsuser 258 | *- Backup*.rdl 259 | 260 | # Microsoft Fakes 261 | FakesAssemblies/ 262 | 263 | # GhostDoc plugin setting file 264 | *.GhostDoc.xml 265 | 266 | # Node.js Tools for Visual Studio 267 | .ntvs_analysis.dat 268 | node_modules/ 269 | 270 | # Visual Studio 6 build log 271 | *.plg 272 | 273 | # Visual Studio 6 workspace options file 274 | *.opt 275 | 276 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 277 | *.vbw 278 | 279 | # Visual Studio LightSwitch build output 280 | **/*.HTMLClient/GeneratedArtifacts 281 | **/*.DesktopClient/GeneratedArtifacts 282 | **/*.DesktopClient/ModelManifest.xml 283 | **/*.Server/GeneratedArtifacts 284 | **/*.Server/ModelManifest.xml 285 | _Pvt_Extensions 286 | 287 | # Paket dependency manager 288 | .paket/paket.exe 289 | paket-files/ 290 | 291 | # FAKE - F# Make 292 | .fake/ 293 | 294 | # JetBrains Rider 295 | .idea/ 296 | *.sln.iml 297 | 298 | # CodeRush personal settings 299 | .cr/personal 300 | 301 | # Python Tools for Visual Studio (PTVS) 302 | __pycache__/ 303 | *.pyc 304 | 305 | # Cake - Uncomment if you are using it 306 | # tools/** 307 | # !tools/packages.config 308 | 309 | # Tabs Studio 310 | *.tss 311 | 312 | # Telerik's JustMock configuration file 313 | *.jmconfig 314 | 315 | # BizTalk build output 316 | *.btp.cs 317 | *.btm.cs 318 | *.odx.cs 319 | *.xsd.cs 320 | 321 | # OpenCover UI analysis results 322 | OpenCover/ 323 | 324 | # Azure Stream Analytics local run output 325 | ASALocalRun/ 326 | 327 | # MSBuild Binary and Structured Log 328 | *.binlog 329 | 330 | # NVidia Nsight GPU debugger configuration file 331 | *.nvuser 332 | 333 | # MFractors (Xamarin productivity tool) working folder 334 | .mfractor/ 335 | 336 | # Local History for Visual Studio 337 | .localhistory/ 338 | 339 | # BeatPulse healthcheck temp database 340 | healthchecksdb 341 | /BlazorSize/scripts/tests/*.js 342 | /BlazorSize/wwwroot/*.js 343 | /BlazorSize.e2eTests/Pages.g.cs 344 | /BlazorSize.e2eTests/Results 345 | /BlazorSize.e2eTests/.ts/project.state 346 | /BlazorSize.e2eTests/Profiler Configurations/{1f06cb7a-bdc7-4bbe-a956-ce0dcffbcd0f}.tsprofconfig 347 | -------------------------------------------------------------------------------- /BlazorPro.BlazorSize.BlazorRepl/BlazorPro.BlazorSize.BlazorRepl.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 6.1.0 5 | net9.0 6 | enable 7 | enable 8 | True 9 | Ed Charbeneau 10 | EdCharbeneau.com 11 | A replacement service only for use with Telerik REPL for Blazor and BlazorSize. This is for demos only and should not be used outside of Telerik REPL for Blazor. 12 | LICENSE.txt 13 | https://github.com/EdCharbeneau/BlazorSize 14 | blazor-glove-128.jpg 15 | 16 | https://github.com/EdCharbeneau/BlazorSize 17 | Blazor, JavaScript Interop 18 | 19 | 6.0.0 Added support for Telerik REPL for Blazor. 20 | 21 | 22 | 23 | 24 | 25 | 26 | True 27 | 28 | 29 | 30 | 31 | True 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /BlazorPro.BlazorSize.BlazorRepl/BlazorReplMediaQueryService.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorPro.BlazorSize 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using Microsoft.JSInterop; 9 | using BlazorPro.BlazorSize; 10 | 11 | public class BlazorReplMediaQueryService : IAsyncDisposable, IMediaQueryService 12 | { 13 | private readonly Lazy> moduleTask; 14 | 15 | public BlazorReplMediaQueryService(IJSRuntime jsRuntime) 16 | { 17 | moduleTask = new(() => jsRuntime.InvokeAsync( 18 | "import", "https://edcharbeneau.com/javascript/blazorSizeMediaModule.js").AsTask()); 19 | } 20 | 21 | // JavaScript uses this value for tracking MediaQueryList instances. 22 | private DotNetObjectReference DotNetInstance { get; set; } = null!; 23 | private readonly List mediaQueries = new(); 24 | public List MediaQueries => mediaQueries; 25 | 26 | private MediaQueryCache? GetMediaQueryFromCache(string byMedia) => mediaQueries?.Find(q => q.MediaRequested == byMedia); 27 | 28 | public void AddQuery(MediaQuery newMediaQuery) 29 | { 30 | var cache = GetMediaQueryFromCache(byMedia: newMediaQuery.Media); 31 | if (cache == null) 32 | { 33 | cache = new MediaQueryCache 34 | { 35 | MediaRequested = newMediaQuery.Media, 36 | }; 37 | mediaQueries.Add(cache); 38 | } 39 | cache.MediaQueries?.Add(newMediaQuery); 40 | } 41 | 42 | public async Task RemoveQuery(MediaQuery mediaQuery) 43 | { 44 | if (mediaQuery == null) return; 45 | 46 | MediaQueryCache? cache = GetMediaQueryFromCache(byMedia: mediaQuery.Media); 47 | 48 | if (cache == null || cache.MediaQueries == null) return; 49 | 50 | try 51 | { 52 | cache.MediaQueries.Remove(mediaQuery); 53 | if (cache.MediaQueries.Any()) return; 54 | mediaQueries.Remove(cache); 55 | IJSObjectReference module = await moduleTask.Value; 56 | await module.InvokeVoidAsync($"removeMediaQuery", DotNetInstance, mediaQuery.InternalMedia.Media); 57 | } 58 | catch (Exception) 59 | { 60 | //https://stackoverflow.com/questions/72488563/blazor-server-side-application-throwing-system-invalidoperationexception-javas 61 | } 62 | } 63 | 64 | public async Task Initialize(MediaQuery mediaQuery) 65 | { 66 | if (mediaQuery?.Media == null) return; 67 | var cache = GetMediaQueryFromCache(byMedia: mediaQuery.Media); 68 | if (cache == null) return; 69 | 70 | if (cache.Value == null) 71 | { 72 | // If we don't flag the cache as loading, duplicate requests will be sent async. 73 | // Duplicate requests = poor performance, esp with web sockets. 74 | if (!cache.Loading) 75 | { 76 | cache.Loading = true; 77 | 78 | var module = await moduleTask.Value; 79 | var task = module.InvokeAsync($"addMediaQueryToList", DotNetInstance, cache.MediaRequested); 80 | cache.Value = await task; 81 | cache.Loading = task.IsCompleted; 82 | // When loading is complete dispatch an update to all subscribers. 83 | if (cache.MediaQueries is null) return; 84 | foreach (var item in cache.MediaQueries) 85 | { 86 | item.MediaQueryChanged(cache.Value); 87 | } 88 | } 89 | } 90 | else 91 | { 92 | var module = await moduleTask.Value; 93 | var task = module.InvokeAsync($"getMediaQueryArgs", cache.MediaRequested); 94 | cache.Value = await task; 95 | 96 | mediaQuery.MediaQueryChanged(cache.Value); 97 | } 98 | } 99 | 100 | public async Task CreateMediaQueryList(DotNetObjectReference dotNetObjectReference) 101 | { 102 | DotNetInstance = dotNetObjectReference; 103 | var module = await moduleTask.Value; 104 | await module.InvokeVoidAsync("addMediaQueryList", dotNetObjectReference); 105 | } 106 | 107 | public async ValueTask DisposeAsync() 108 | { 109 | if (DotNetInstance is not null) 110 | { 111 | var module = await moduleTask.Value; 112 | await module.InvokeVoidAsync("removeMediaQueryList", DotNetInstance); 113 | DotNetInstance.Dispose(); 114 | GC.SuppressFinalize(this); 115 | } 116 | } 117 | } 118 | } -------------------------------------------------------------------------------- /BlazorPro.BlazorSize.BlazorRepl/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2022 Ed Charbeneau 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at 4 | 5 | http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. -------------------------------------------------------------------------------- /BlazorPro.BlazorSize.Bunit/BlazorPro.BlazorSize.Bunit.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /BlazorPro.BlazorSize.Bunit/BunitExtensions.cs: -------------------------------------------------------------------------------- 1 | using BlazorPro.BlazorSize; 2 | using Microsoft.Extensions.DependencyInjection; 3 | 4 | namespace Bunit 5 | { 6 | public static class BunitExtensions 7 | { 8 | public static FakeMediaQueryService AddBlazorSize(this TestContextBase testContext) 9 | { 10 | testContext.RenderTree.Add(); 11 | var mediaQueryServices = new FakeMediaQueryService(testContext); 12 | testContext.Services.AddSingleton(mediaQueryServices); 13 | return mediaQueryServices; 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /BlazorPro.BlazorSize.Bunit/FakeMediaQueryService.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | using BlazorPro.BlazorSize; 4 | using Bunit; 5 | using Microsoft.JSInterop; 6 | 7 | namespace Bunit 8 | { 9 | public class FakeMediaQueryService : IMediaQueryService 10 | { 11 | private readonly TestContextBase contextBase; 12 | private MediaQueryArgs? activeMediaQuery = null; 13 | 14 | public List MediaQueries { get; } = new(); 15 | 16 | public FakeMediaQueryService(TestContextBase contextBase) 17 | { 18 | this.contextBase = contextBase; 19 | } 20 | 21 | public void AddQuery(MediaQuery newMediaQuery) 22 | { 23 | var mediaQueryCache = GetMediaQueryFromCache(newMediaQuery.Media); 24 | 25 | if (mediaQueryCache is null) 26 | { 27 | mediaQueryCache = new MediaQueryCache() 28 | { 29 | MediaRequested = newMediaQuery.Media 30 | }; 31 | MediaQueries.Add(mediaQueryCache); 32 | } 33 | 34 | mediaQueryCache.MediaQueries.Add(newMediaQuery); 35 | 36 | SetActiveMediaQuery(activeMediaQuery); 37 | } 38 | 39 | public Task CreateMediaQueryList(DotNetObjectReference dotNetObjectReference) 40 | => Task.CompletedTask; 41 | 42 | public Task Initialize(MediaQuery mediaQuery) 43 | => Task.CompletedTask; 44 | 45 | public Task RemoveQuery(MediaQuery mediaQuery) 46 | { 47 | var mediaQueryFromCache = GetMediaQueryFromCache(mediaQuery.Media); 48 | if (mediaQueryFromCache is null) 49 | return Task.CompletedTask; 50 | 51 | mediaQueryFromCache.MediaQueries.Remove(mediaQuery); 52 | 53 | if (mediaQueryFromCache.MediaQueries.Count == 0) 54 | MediaQueries.Remove(mediaQueryFromCache); 55 | 56 | return Task.CompletedTask; 57 | } 58 | 59 | public void SetActiveBreakPoint(string breakpoint) 60 | => SetActiveMediaQuery(new MediaQueryArgs 61 | { 62 | Matches = true, 63 | Media = breakpoint 64 | }); 65 | 66 | public void SetActiveMediaQuery(MediaQueryArgs args) 67 | { 68 | if (args is null) 69 | return; 70 | 71 | activeMediaQuery = args; 72 | 73 | // cache must be compared by actual value, not RequestedMedia when invoked from JavaScript 74 | // DOM Media value my be different that the initally requested media query value. 75 | var cache = GetMediaQueryFromCache(args.Media); 76 | 77 | if (cache is null) return; 78 | 79 | // Dispatch events to all subscribers 80 | contextBase.Renderer.Dispatcher.InvokeAsync(() => 81 | { 82 | foreach (var item in cache.MediaQueries) 83 | { 84 | item.MediaQueryChanged(args); 85 | } 86 | }); 87 | } 88 | 89 | private MediaQueryCache GetMediaQueryFromCache(string byMedia) 90 | => MediaQueries?.Find(q => q.MediaRequested == byMedia); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /BlazorSize.UnitTesting/BlazorPro.BlazorSize.UnitTesting.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net9.0 5 | enable 6 | enable 7 | True 8 | latest 9 | 6.0.0 10 | Ed Charbeneau 11 | EdCharbeneau.com 12 | A testing library to provide helper extensions for BlazorSize. 13 | LICENSE.txt 14 | https://github.com/EdCharbeneau/BlazorSize 15 | blazor-glove-128.jpg 16 | 17 | https://github.com/EdCharbeneau/BlazorSize 18 | Blazor, JavaScript Interop 19 | 20 | 6.0.0 Added support for bUnit. 21 | 22 | 23 | 24 | 25 | 26 | 27 | True 28 | 29 | 30 | 31 | 32 | True 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /BlazorSize.UnitTesting/Bunit/BunitExtensions.cs: -------------------------------------------------------------------------------- 1 | using BlazorPro.BlazorSize; 2 | using Microsoft.Extensions.DependencyInjection; 3 | 4 | namespace Bunit 5 | { 6 | public static class BunitExtensions 7 | { 8 | public static FakeMediaQueryService AddBlazorSize(this TestContextBase testContext) 9 | { 10 | testContext.RenderTree.Add(); 11 | var mediaQueryServices = new FakeMediaQueryService(testContext); 12 | testContext.Services.AddSingleton(mediaQueryServices); 13 | return mediaQueryServices; 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /BlazorSize.UnitTesting/Bunit/FakeMediaQueryService.cs: -------------------------------------------------------------------------------- 1 | using BlazorPro.BlazorSize; 2 | using Microsoft.JSInterop; 3 | 4 | namespace Bunit 5 | { 6 | public class FakeMediaQueryService : IMediaQueryService 7 | { 8 | private readonly TestContextBase contextBase; 9 | private MediaQueryArgs activeMediaQuery = null!; 10 | 11 | public List MediaQueries { get; } = new(); 12 | 13 | public FakeMediaQueryService(TestContextBase contextBase) 14 | { 15 | this.contextBase = contextBase; 16 | } 17 | 18 | public void AddQuery(MediaQuery newMediaQuery) 19 | { 20 | var mediaQueryCache = GetMediaQueryFromCache(newMediaQuery.Media); 21 | 22 | if (mediaQueryCache is null) 23 | { 24 | mediaQueryCache = new MediaQueryCache() 25 | { 26 | MediaRequested = newMediaQuery.Media 27 | }; 28 | MediaQueries.Add(mediaQueryCache); 29 | } 30 | 31 | mediaQueryCache.MediaQueries.Add(newMediaQuery); 32 | 33 | SetActiveMediaQuery(activeMediaQuery); 34 | } 35 | 36 | public Task CreateMediaQueryList(DotNetObjectReference dotNetObjectReference) 37 | => Task.CompletedTask; 38 | 39 | public Task Initialize(MediaQuery mediaQuery) 40 | => Task.CompletedTask; 41 | 42 | public Task RemoveQuery(MediaQuery mediaQuery) 43 | { 44 | var mediaQueryFromCache = GetMediaQueryFromCache(mediaQuery.Media); 45 | if (mediaQueryFromCache is null) 46 | return Task.CompletedTask; 47 | 48 | mediaQueryFromCache.MediaQueries.Remove(mediaQuery); 49 | 50 | if (mediaQueryFromCache.MediaQueries.Count == 0) 51 | MediaQueries.Remove(mediaQueryFromCache); 52 | 53 | return Task.CompletedTask; 54 | } 55 | 56 | public void SetActiveBreakPoint(string breakpoint) 57 | => SetActiveMediaQuery(new MediaQueryArgs 58 | { 59 | Matches = true, 60 | Media = breakpoint 61 | }); 62 | 63 | public void SetActiveMediaQuery(MediaQueryArgs args) 64 | { 65 | if (args.Media == null) return; 66 | 67 | activeMediaQuery = args; 68 | 69 | // cache must be compared by actual value, not RequestedMedia when invoked from JavaScript 70 | // DOM Media value my be different that the initally requested media query value. 71 | var cache = GetMediaQueryFromCache(args.Media); 72 | 73 | if (cache is null) return; 74 | 75 | // Dispatch events to all subscribers 76 | contextBase.Renderer.Dispatcher.InvokeAsync(() => 77 | { 78 | foreach (var item in cache.MediaQueries) 79 | { 80 | item.MediaQueryChanged(args); 81 | } 82 | }); 83 | } 84 | 85 | private MediaQueryCache? GetMediaQueryFromCache(string byMedia) 86 | => MediaQueries?.Find(q => q.MediaRequested == byMedia); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /BlazorSize.UnitTesting/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2019 Ed Charbeneau 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at 4 | 5 | http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. -------------------------------------------------------------------------------- /BlazorSize.e2eTests/BlazorSize Adaptive Table and Template.imgstore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EdCharbeneau/BlazorSize/59af9c85463a6a675a91cfc957bd0a81f2dd8209/BlazorSize.e2eTests/BlazorSize Adaptive Table and Template.imgstore -------------------------------------------------------------------------------- /BlazorSize.e2eTests/BlazorSize Adaptive Table and Template.tstest.cs: -------------------------------------------------------------------------------- 1 | using Telerik.TestStudio.Translators.Common; 2 | using Telerik.TestingFramework.Controls.TelerikUI.Blazor; 3 | using Telerik.TestingFramework.Controls.KendoUI.Angular; 4 | using Telerik.TestingFramework.Controls.KendoUI; 5 | using Telerik.WebAii.Controls.Html; 6 | using Telerik.WebAii.Controls.Xaml; 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | using System.Linq; 11 | 12 | using ArtOfTest.Common.UnitTesting; 13 | using ArtOfTest.WebAii.Core; 14 | using ArtOfTest.WebAii.Controls.HtmlControls; 15 | using ArtOfTest.WebAii.Controls.HtmlControls.HtmlAsserts; 16 | using ArtOfTest.WebAii.Design; 17 | using ArtOfTest.WebAii.Design.Execution; 18 | using ArtOfTest.WebAii.ObjectModel; 19 | using ArtOfTest.WebAii.Silverlight; 20 | using ArtOfTest.WebAii.Silverlight.UI; 21 | 22 | namespace BlazorSize.e2eTests 23 | { 24 | 25 | public class BlazorSize_Adaptive_Table_and_Template : BaseWebAiiTest 26 | { 27 | #region [ Dynamic Pages Reference ] 28 | 29 | private Pages _pages; 30 | 31 | /// 32 | /// Gets the Pages object that has references 33 | /// to all the elements, frames or regions 34 | /// in this project. 35 | /// 36 | public Pages Pages 37 | { 38 | get 39 | { 40 | if (_pages == null) 41 | { 42 | _pages = new Pages(Manager.Current); 43 | } 44 | return _pages; 45 | } 46 | } 47 | 48 | #endregion 49 | 50 | // Add your test methods here... 51 | 52 | [CodedStep(@"Open Desktop")] 53 | public void OpenWin() 54 | { 55 | Actions.InvokeScript(@"openWin(""fetchdata"", 1920, 1080)"); 56 | 57 | } 58 | 59 | [CodedStep(@"Resize browser to small")] 60 | public void Resize_Mobile() 61 | { 62 | Manager.Browsers[0].Actions.InvokeScript(@"resizeWin(560,800)"); 63 | 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /BlazorSize.e2eTests/BlazorSize Construct & Dispose.imgstore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EdCharbeneau/BlazorSize/59af9c85463a6a675a91cfc957bd0a81f2dd8209/BlazorSize.e2eTests/BlazorSize Construct & Dispose.imgstore -------------------------------------------------------------------------------- /BlazorSize.e2eTests/BlazorSize Construct & Dispose.tstest: -------------------------------------------------------------------------------- 1 | { 2 | "__type": "ArtOfTest.WebAii.Design.ProjectModel.Test", 3 | "__value": { 4 | "DomStatesCounter": {}, 5 | "AppStartSkip": false, 6 | "WebKitExecutionDelay": 0, 7 | "ReuseAppWindow": 0, 8 | "ProfilerResultsDirectory": null, 9 | "ProfilerBenchmarkResultsFilePath": null, 10 | "ProfilerBenchmarkResultsId": "00000000-0000-0000-0000-000000000000", 11 | "ProfilerConfigurationId": "00000000-0000-0000-0000-000000000000", 12 | "ProfilerConfigurations": {}, 13 | "CurrentReferences": [ 14 | "System", 15 | "System.Core", 16 | "ArtOfTest.WebAii, Version=2020.3.1209.0, Culture=neutral, PublicKeyToken=4fd5f65be123776c", 17 | "ArtOfTest.WebAii.Design, Version=2020.3.1209.0, Culture=neutral, PublicKeyToken=4fc62bbc3827ab1d", 18 | "Telerik.WebAii.Controls.Html, Version=2020.3.1209.0, Culture=neutral, PublicKeyToken=528163f3e645de45", 19 | "Telerik.WebAii.Controls.Xaml, Version=2020.3.1209.0, Culture=neutral, PublicKeyToken=528163f3e645de45", 20 | "Telerik.WebAii.Controls.Xaml.Wpf, Version=2020.3.1209.0, Culture=neutral, PublicKeyToken=528163f3e645de45", 21 | "Telerik.TestingFramework.Controls.KendoUI, Version=2020.3.1209.0, Culture=neutral, PublicKeyToken=528163f3e645de45", 22 | "Telerik.TestingFramework.Controls.KendoUI.Angular, Version=2020.3.1209.0, Culture=neutral, PublicKeyToken=528163f3e645de45", 23 | "Telerik.TestingFramework.Controls.TelerikUI.Blazor, Version=2020.3.1209.0, Culture=neutral, PublicKeyToken=528163f3e645de45", 24 | "Telerik.TestStudio.Translators.Common, Version=2020.3.1209.0, Culture=neutral, PublicKeyToken=528163f3e645de45", 25 | "..\\..\\..\\..\\..\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Enterprise\\Common7\\IDE\\PublicAssemblies\\Microsoft.VisualStudio.QualityTools.UnitTestFramework.dll", 26 | "System.Windows.Forms" 27 | ], 28 | "Steps": { 29 | "__type": "ArtOfTest.Common.Design.ProjectModel.AutomationStepList", 30 | "__value": [ 31 | { 32 | "__type": "ArtOfTest.WebAii.Design.ProjectModel.AutomationStep", 33 | "__value": { 34 | "Runtime ID": "a234ff09-491b-465f-a668-5687511caefd", 35 | "Description": "Clear Browser Cache", 36 | "CustomDescription": null, 37 | "Step": { 38 | "__type": "ArtOfTest.WebAii.Design.IntrinsicTranslators.Descriptors.ClearCookiesActionDescriptor", 39 | "__value": { 40 | "ClearCookies": true, 41 | "ClearTempFiles": true, 42 | "SearchByImageFirst": 0, 43 | "ClearHistory": true, 44 | "WaitOnElements": false, 45 | "WaitOnElementsTimeout": 0, 46 | "UseStepWaitOnElementsTimout": false, 47 | "UsejQuery": false, 48 | "ClosesBrowser": false, 49 | "AjaxTimeout": 0, 50 | "Pause": 0, 51 | "RunsAgainstVersion": "", 52 | "Constraints": { 53 | "__type": "ArtOfTest.Common.Design.Extensibility.Descriptors.DescriptorConstraints", 54 | "__value": { 55 | "ConstraintValues": {} 56 | } 57 | }, 58 | "Elements": {}, 59 | "StepType": 4, 60 | "LogMessageOnFailure": "", 61 | "EditorType": "", 62 | "DataBindingExpressions": {} 63 | } 64 | }, 65 | "Enabled": true, 66 | "Order": 1, 67 | "Id": "", 68 | "StepState": 0, 69 | "ContinueOnFailure": false, 70 | "StepImageKey": "Test BlazorSize Construct & Disposei0wcepfh.5vo", 71 | "QcId": 0, 72 | "QcVersionStamp": 0, 73 | "Version": 200 74 | } 75 | }, 76 | { 77 | "__type": "ArtOfTest.WebAii.Design.ProjectModel.AutomationStep", 78 | "__value": { 79 | "Runtime ID": "06adfa6b-de0f-4ba7-9f59-f4f8c9701ae9", 80 | "Description": "Navigate to : ''", 81 | "CustomDescription": "Navigate to : UUT", 82 | "Step": { 83 | "__type": "ArtOfTest.WebAii.Design.IntrinsicTranslators.Descriptors.NavigateToActionDescriptor", 84 | "__value": { 85 | "UseDecodedUrl": true, 86 | "NavigateURL": "", 87 | "BaseUrl": "~", 88 | "SearchByImageFirst": 0, 89 | "WaitOnElementsTimeout": 0, 90 | "UseStepWaitOnElementsTimout": false, 91 | "WaitOnElements": false, 92 | "UsejQuery": false, 93 | "ClosesBrowser": false, 94 | "AjaxTimeout": 0, 95 | "Pause": 0, 96 | "RunsAgainstVersion": "", 97 | "Constraints": { 98 | "__type": "ArtOfTest.Common.Design.Extensibility.Descriptors.DescriptorConstraints", 99 | "__value": { 100 | "ConstraintValues": {} 101 | } 102 | }, 103 | "Elements": {}, 104 | "StepType": 4, 105 | "LogMessageOnFailure": "", 106 | "EditorType": "", 107 | "DataBindingExpressions": {} 108 | } 109 | }, 110 | "Enabled": true, 111 | "Order": 2, 112 | "Id": "", 113 | "StepState": 0, 114 | "ContinueOnFailure": false, 115 | "StepImageKey": "WebTestwxg2rdvl.m2c", 116 | "QcId": 0, 117 | "QcVersionStamp": 0, 118 | "Version": 200 119 | } 120 | }, 121 | { 122 | "__type": "ArtOfTest.WebAii.Design.ProjectModel.AutomationStep", 123 | "__value": { 124 | "Runtime ID": "d6366de8-8dbb-4424-80aa-de91cfa86b6f", 125 | "Description": "Click 'TestDisposeLink'", 126 | "CustomDescription": null, 127 | "Step": { 128 | "__type": "ArtOfTest.WebAii.Design.IntrinsicTranslators.Descriptors.ClickActionDescriptor", 129 | "__value": { 130 | "SimulateRealClick": true, 131 | "ScrollToVisibleType": 2, 132 | "OffSet": { 133 | "__type": "ArtOfTest.Common.Design.ActionPoint", 134 | "__value": "0, 0, Percentage, AbsoluteCenter" 135 | }, 136 | "UsejQuery": false, 137 | "ClosesBrowser": false, 138 | "AjaxTimeout": 0, 139 | "Pause": 0, 140 | "WaitOnElementsTimeout": 30000, 141 | "SearchByImageFirst": 0, 142 | "UseStepWaitOnElementsTimout": false, 143 | "WaitOnElements": true, 144 | "RunsAgainstVersion": "", 145 | "Constraints": { 146 | "__type": "ArtOfTest.Common.Design.Extensibility.Descriptors.DescriptorConstraints", 147 | "__value": { 148 | "ConstraintValues": {} 149 | } 150 | }, 151 | "Elements": { 152 | "target": { 153 | "__type": "ArtOfTest.Common.Design.Extensibility.Descriptors.ElementDescriptor", 154 | "__value": { 155 | "Name": "target", 156 | "Region": null, 157 | "Page": { 158 | "__type": "ArtOfTest.WebAii.Design.ProjectModel.PageUri", 159 | "__value": { 160 | "Path": "https://localhost:3001/fetchdata", 161 | "Query": "", 162 | "BaseUrl": "", 163 | "EquivalentUrls": [], 164 | "Fragment": "", 165 | "Title": "BlazorSize.ExampleNet30", 166 | "CompareMode": 2, 167 | "AlwaysUseTitleInCompare": false, 168 | "FriendlyName": "BlazorSizeExampleNet300" 169 | } 170 | }, 171 | "Frame": null, 172 | "FindExpression": { 173 | "__type": "ArtOfTest.WebAii.Design.ProjectModel.HtmlFindExpressionElement", 174 | "__value": { 175 | "uniqueKey": "NBzChnnFYwZAScbK4jnSwxhUUpQztUnxarOqJ0H9xV4=", 176 | "NodeKind": 4, 177 | "TechnologyType": 1, 178 | "Name": "TestDisposeLink", 179 | "FindExpressions": [ 180 | { 181 | "Key": 1, 182 | "Value": { 183 | "__type": "ArtOfTest.WebAii.Core.HtmlFindExpression", 184 | "__value": { 185 | "Clauses": [ 186 | { 187 | "__type": "ArtOfTest.WebAii.Core.HtmlFindClause", 188 | "__value": { 189 | "Clause": "href=blank" 190 | } 191 | }, 192 | { 193 | "__type": "ArtOfTest.WebAii.Core.HtmlFindClause", 194 | "__value": { 195 | "Clause": "tagname=a" 196 | } 197 | } 198 | ], 199 | "Constraints": [], 200 | "ChainStops": [] 201 | } 202 | } 203 | } 204 | ], 205 | "BackupSearchClause": "/html[1]/body[1]/app[1]/div[1]/div[2]/ul[1]/li[6]/a[1]", 206 | "ImageSearchClauses": [ 207 | { 208 | "__type": "ArtOfTest.Common.ImageSearchClause", 209 | "__value": { 210 | "Resolution": "1920, 1080", 211 | "BroweserType": 10, 212 | "Image": "2C35F85E64F13A60D9BBF8EFDD4B3942F48458FEFA7CF960B3AB59A0EF9EDDC7", 213 | "Index": 0, 214 | "MatchThreshold": 90, 215 | "OsVersion": "10.0.19041.0", 216 | "ScreenScale": 1.0, 217 | "ScrollOnImageSearch": null 218 | } 219 | } 220 | ], 221 | "DataBindingExpressions": {} 222 | } 223 | }, 224 | "HtmlControlType": "ArtOfTest.WebAii.Controls.HtmlControls.HtmlAnchor", 225 | "IsIndependent": false, 226 | "DomStateKey": "", 227 | "TechnologyType": 1, 228 | "Version": 200 229 | } 230 | } 231 | }, 232 | "StepType": 4, 233 | "LogMessageOnFailure": "", 234 | "EditorType": "", 235 | "DataBindingExpressions": {} 236 | } 237 | }, 238 | "Enabled": true, 239 | "Order": 3, 240 | "Id": "", 241 | "StepState": 0, 242 | "ContinueOnFailure": false, 243 | "StepImageKey": "WebTestn4wwkcqk.gih", 244 | "QcId": 0, 245 | "QcVersionStamp": 0, 246 | "Version": 200 247 | } 248 | }, 249 | { 250 | "__type": "ArtOfTest.WebAii.Design.ProjectModel.AutomationStep", 251 | "__value": { 252 | "Runtime ID": "4d28e1a1-0a02-4084-898b-247f1a90e8d1", 253 | "Description": "Verify that there are no JS console errors on the current page.", 254 | "CustomDescription": null, 255 | "Step": { 256 | "__type": "ArtOfTest.WebAii.Design.IntrinsicTranslators.Descriptors.JsConsoleErrorsVerificationDescriptor", 257 | "__value": { 258 | "WaitOnElements": false, 259 | "WaitOnElementsTimeout": 0, 260 | "UseStepWaitOnElementsTimout": false, 261 | "SearchByImageFirst": 0, 262 | "ExcludedErrors": "", 263 | "DataBindVariableName": null, 264 | "Timeout": 30000, 265 | "CheckInterval": 500, 266 | "EnsureStateIsCurrent": false, 267 | "Pause": 0, 268 | "RunsAgainstVersion": "", 269 | "Constraints": { 270 | "__type": "ArtOfTest.Common.Design.Extensibility.Descriptors.DescriptorConstraints", 271 | "__value": { 272 | "ConstraintValues": {} 273 | } 274 | }, 275 | "Elements": {}, 276 | "StepType": 2, 277 | "LogMessageOnFailure": "", 278 | "EditorType": "", 279 | "DataBindingExpressions": {} 280 | } 281 | }, 282 | "Enabled": true, 283 | "Order": 4, 284 | "Id": "", 285 | "StepState": 0, 286 | "ContinueOnFailure": false, 287 | "StepImageKey": "WebTesthoqj4ulr.yhf", 288 | "QcId": 0, 289 | "QcVersionStamp": 0, 290 | "Version": 200 291 | } 292 | }, 293 | { 294 | "__type": "ArtOfTest.WebAii.Design.ProjectModel.AutomationStep", 295 | "__value": { 296 | "Runtime ID": "035808f2-8aa1-4cc9-988a-81d26b6795ec", 297 | "Description": "Click 'FetchDataLink'", 298 | "CustomDescription": null, 299 | "Step": { 300 | "__type": "ArtOfTest.WebAii.Design.IntrinsicTranslators.Descriptors.ClickActionDescriptor", 301 | "__value": { 302 | "SimulateRealClick": true, 303 | "ScrollToVisibleType": 2, 304 | "OffSet": { 305 | "__type": "ArtOfTest.Common.Design.ActionPoint", 306 | "__value": "0, 0, Percentage, AbsoluteCenter" 307 | }, 308 | "UsejQuery": false, 309 | "ClosesBrowser": false, 310 | "AjaxTimeout": 0, 311 | "Pause": 0, 312 | "WaitOnElementsTimeout": 30000, 313 | "SearchByImageFirst": 0, 314 | "UseStepWaitOnElementsTimout": false, 315 | "WaitOnElements": true, 316 | "RunsAgainstVersion": "", 317 | "Constraints": { 318 | "__type": "ArtOfTest.Common.Design.Extensibility.Descriptors.DescriptorConstraints", 319 | "__value": { 320 | "ConstraintValues": {} 321 | } 322 | }, 323 | "Elements": { 324 | "target": { 325 | "__type": "ArtOfTest.Common.Design.Extensibility.Descriptors.ElementDescriptor", 326 | "__value": { 327 | "Name": "target", 328 | "Region": null, 329 | "Page": { 330 | "__type": "ArtOfTest.WebAii.Design.ProjectModel.PageUri", 331 | "__value": { 332 | "Path": "https://localhost:3001/blank", 333 | "Query": "", 334 | "BaseUrl": "", 335 | "EquivalentUrls": [], 336 | "Fragment": "", 337 | "Title": "BlazorSize.ExampleNet30", 338 | "CompareMode": 2, 339 | "AlwaysUseTitleInCompare": false, 340 | "FriendlyName": "BlazorSizeExampleNet301" 341 | } 342 | }, 343 | "Frame": null, 344 | "FindExpression": { 345 | "__type": "ArtOfTest.WebAii.Design.ProjectModel.HtmlFindExpressionElement", 346 | "__value": { 347 | "uniqueKey": "/2ti0epSUj9L8N9PKVg4wOzJpaksVni7Vv3uEYuhEBM=", 348 | "NodeKind": 4, 349 | "TechnologyType": 1, 350 | "Name": "FetchDataLink", 351 | "FindExpressions": [ 352 | { 353 | "Key": 1, 354 | "Value": { 355 | "__type": "ArtOfTest.WebAii.Core.HtmlFindExpression", 356 | "__value": { 357 | "Clauses": [ 358 | { 359 | "__type": "ArtOfTest.WebAii.Core.HtmlFindClause", 360 | "__value": { 361 | "Clause": "href=fetchdata" 362 | } 363 | }, 364 | { 365 | "__type": "ArtOfTest.WebAii.Core.HtmlFindClause", 366 | "__value": { 367 | "Clause": "tagname=a" 368 | } 369 | } 370 | ], 371 | "Constraints": [], 372 | "ChainStops": [] 373 | } 374 | } 375 | } 376 | ], 377 | "BackupSearchClause": "/html[1]/body[1]/app[1]/div[1]/div[2]/ul[1]/li[2]/a[1]", 378 | "ImageSearchClauses": [ 379 | { 380 | "__type": "ArtOfTest.Common.ImageSearchClause", 381 | "__value": { 382 | "Resolution": "1920, 1080", 383 | "BroweserType": 10, 384 | "Image": "1287A8B192D7375192F25A325183ADC4C272D239E7FA33CD83B9B1E9B24A599E", 385 | "Index": 0, 386 | "MatchThreshold": 90, 387 | "OsVersion": "10.0.19041.0", 388 | "ScreenScale": 1.0, 389 | "ScrollOnImageSearch": null 390 | } 391 | } 392 | ], 393 | "DataBindingExpressions": {} 394 | } 395 | }, 396 | "HtmlControlType": "ArtOfTest.WebAii.Controls.HtmlControls.HtmlAnchor", 397 | "IsIndependent": false, 398 | "DomStateKey": "", 399 | "TechnologyType": 1, 400 | "Version": 200 401 | } 402 | } 403 | }, 404 | "StepType": 4, 405 | "LogMessageOnFailure": "", 406 | "EditorType": "", 407 | "DataBindingExpressions": {} 408 | } 409 | }, 410 | "Enabled": true, 411 | "Order": 5, 412 | "Id": "", 413 | "StepState": 0, 414 | "ContinueOnFailure": false, 415 | "StepImageKey": "WebTestz5xzz3ml.ktr", 416 | "QcId": 0, 417 | "QcVersionStamp": 0, 418 | "Version": 200 419 | } 420 | }, 421 | { 422 | "__type": "ArtOfTest.WebAii.Design.ProjectModel.AutomationStep", 423 | "__value": { 424 | "Runtime ID": "9e77515e-ee96-498c-b537-5cbfd3fe63ec", 425 | "Description": "Verify that there are no JS console errors on the current page.", 426 | "CustomDescription": null, 427 | "Step": { 428 | "__type": "ArtOfTest.WebAii.Design.IntrinsicTranslators.Descriptors.JsConsoleErrorsVerificationDescriptor", 429 | "__value": { 430 | "WaitOnElements": false, 431 | "WaitOnElementsTimeout": 0, 432 | "UseStepWaitOnElementsTimout": false, 433 | "SearchByImageFirst": 0, 434 | "ExcludedErrors": "", 435 | "DataBindVariableName": null, 436 | "Timeout": 30000, 437 | "CheckInterval": 500, 438 | "EnsureStateIsCurrent": false, 439 | "Pause": 0, 440 | "RunsAgainstVersion": "", 441 | "Constraints": { 442 | "__type": "ArtOfTest.Common.Design.Extensibility.Descriptors.DescriptorConstraints", 443 | "__value": { 444 | "ConstraintValues": {} 445 | } 446 | }, 447 | "Elements": {}, 448 | "StepType": 2, 449 | "LogMessageOnFailure": "", 450 | "EditorType": "", 451 | "DataBindingExpressions": {} 452 | } 453 | }, 454 | "Enabled": true, 455 | "Order": 6, 456 | "Id": "", 457 | "StepState": 0, 458 | "ContinueOnFailure": false, 459 | "StepImageKey": "WebTestv5creis0.giq", 460 | "QcId": 0, 461 | "QcVersionStamp": 0, 462 | "Version": 200 463 | } 464 | }, 465 | { 466 | "__type": "ArtOfTest.WebAii.Design.ProjectModel.AutomationStep", 467 | "__value": { 468 | "Runtime ID": "578a02d1-4d2c-431a-b729-e5078772e082", 469 | "Description": "Click 'Div'", 470 | "CustomDescription": null, 471 | "Step": { 472 | "__type": "ArtOfTest.WebAii.Design.IntrinsicTranslators.Descriptors.ClickActionDescriptor", 473 | "__value": { 474 | "SimulateRealClick": true, 475 | "ScrollToVisibleType": 2, 476 | "OffSet": { 477 | "__type": "ArtOfTest.Common.Design.ActionPoint", 478 | "__value": "0, 0, Percentage, AbsoluteCenter" 479 | }, 480 | "UsejQuery": false, 481 | "ClosesBrowser": false, 482 | "AjaxTimeout": 0, 483 | "Pause": 0, 484 | "WaitOnElementsTimeout": 30000, 485 | "SearchByImageFirst": 0, 486 | "UseStepWaitOnElementsTimout": false, 487 | "WaitOnElements": true, 488 | "RunsAgainstVersion": "", 489 | "Constraints": { 490 | "__type": "ArtOfTest.Common.Design.Extensibility.Descriptors.DescriptorConstraints", 491 | "__value": { 492 | "ConstraintValues": {} 493 | } 494 | }, 495 | "Elements": { 496 | "target": { 497 | "__type": "ArtOfTest.Common.Design.Extensibility.Descriptors.ElementDescriptor", 498 | "__value": { 499 | "Name": "target", 500 | "Region": null, 501 | "Page": { 502 | "__type": "ArtOfTest.WebAii.Design.ProjectModel.PageUri", 503 | "__value": { 504 | "Path": "https://localhost:3001/fetchdata", 505 | "Query": "", 506 | "BaseUrl": "", 507 | "EquivalentUrls": [], 508 | "Fragment": "", 509 | "Title": "BlazorSize.ExampleNet30", 510 | "CompareMode": 2, 511 | "AlwaysUseTitleInCompare": false, 512 | "FriendlyName": "BlazorSizeExampleNet300" 513 | } 514 | }, 515 | "Frame": null, 516 | "FindExpression": { 517 | "__type": "ArtOfTest.WebAii.Design.ProjectModel.HtmlFindExpressionElement", 518 | "__value": { 519 | "uniqueKey": "PWvK5XdBu6xfGnnJEanmXSDU6UjMtybtzCluPBp9W0k=", 520 | "NodeKind": 4, 521 | "TechnologyType": 1, 522 | "Name": "Div", 523 | "FindExpressions": [ 524 | { 525 | "Key": 1, 526 | "Value": { 527 | "__type": "ArtOfTest.WebAii.Core.HtmlFindExpression", 528 | "__value": { 529 | "Clauses": [ 530 | { 531 | "__type": "ArtOfTest.WebAii.Core.HtmlFindClause", 532 | "__value": { 533 | "Clause": "TagIndex=div:0" 534 | } 535 | } 536 | ], 537 | "Constraints": [], 538 | "ChainStops": [] 539 | } 540 | } 541 | } 542 | ], 543 | "BackupSearchClause": "/html[1]/body[1]/app[1]/div[1]", 544 | "ImageSearchClauses": [ 545 | { 546 | "__type": "ArtOfTest.Common.ImageSearchClause", 547 | "__value": { 548 | "Resolution": "1920, 1080", 549 | "BroweserType": 10, 550 | "Image": "33C61E627EF73766BBAA3769E01C8D16C4B271508A7B3C79FCCD8E7374AF4B15", 551 | "Index": 0, 552 | "MatchThreshold": 90, 553 | "OsVersion": "10.0.19041.0", 554 | "ScreenScale": 1.0, 555 | "ScrollOnImageSearch": null 556 | } 557 | } 558 | ], 559 | "DataBindingExpressions": {} 560 | } 561 | }, 562 | "HtmlControlType": "ArtOfTest.WebAii.Controls.HtmlControls.HtmlDiv", 563 | "IsIndependent": false, 564 | "DomStateKey": "", 565 | "TechnologyType": 1, 566 | "Version": 200 567 | } 568 | } 569 | }, 570 | "StepType": 4, 571 | "LogMessageOnFailure": "", 572 | "EditorType": "", 573 | "DataBindingExpressions": {} 574 | } 575 | }, 576 | "Enabled": true, 577 | "Order": 7, 578 | "Id": "", 579 | "StepState": 0, 580 | "ContinueOnFailure": false, 581 | "StepImageKey": "WebTestxhnikdfh.fjv", 582 | "QcId": 0, 583 | "QcVersionStamp": 0, 584 | "Version": 200 585 | } 586 | } 587 | ] 588 | }, 589 | "IndependentDescriptors": [], 590 | "HtmlDescription": "", 591 | "SilverlightEnabled": false, 592 | "IsSilverlightApp": false, 593 | "SilverlightAppWebUrl": "", 594 | "SilverlightAppLocalDirectory": "", 595 | "SilverlightAppRecordLocal": false, 596 | "SilverlightAppOriginUri": null, 597 | "WpfAppPath": null, 598 | "AppStartArgs": null, 599 | "UseDefaultWpfAppPath": false, 600 | "DesktopAppPath": null, 601 | "UseDefaultDesktopAppPath": false, 602 | "TestAssembly": "BlazorSize.e2eTests.dll", 603 | "TestAssemblyPath": "bin", 604 | "TestClass": "", 605 | "ResourceFilePath": "BlazorSize Construct & Dispose.resx", 606 | "ElementImageStoreFilePath": "BlazorSize Construct & Dispose.imgstore", 607 | "HasCodeBehind": false, 608 | "DataInfo": { 609 | "__type": "ArtOfTest.Common.Design.Data.DataInfo", 610 | "__value": { 611 | "ConnectionString": "", 612 | "DefaultToGrid": true, 613 | "DataRange": "", 614 | "LoadRecordsCount": null, 615 | "DataTableName": "", 616 | "DataProvider": "", 617 | "DataEnabled": true, 618 | "HasBuiltinGrid": false, 619 | "DataType": 4, 620 | "BuiltInData": null, 621 | "BuiltInDocumentKey": null, 622 | "TSQL": "" 623 | } 624 | }, 625 | "VSProperties": {}, 626 | "DeploymentItems": [], 627 | "IsTestFragment": false, 628 | "StopTestListOnFailure": false, 629 | "InheritParentDataSource": false, 630 | "BrowserType": 0, 631 | "TestLinks": [], 632 | "Description": "", 633 | "Name": "BlazorSize Construct & Dispose", 634 | "Owner": "", 635 | "Path": "BlazorSize Construct & Dispose.tstest", 636 | "Priority": 0, 637 | "TestType": 0, 638 | "UniqueId": "4606a41e-798b-45f4-af04-539f9e05d35e", 639 | "Id": "4606a41e-798b-45f4-af04-539f9e05d35e", 640 | "ProjectId": "1f8b050d-e725-4ceb-b9a0-6eab12b5769b", 641 | "Version": 210 642 | } 643 | } -------------------------------------------------------------------------------- /BlazorSize.e2eTests/BlazorSize Size Reverse.imgstore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EdCharbeneau/BlazorSize/59af9c85463a6a675a91cfc957bd0a81f2dd8209/BlazorSize.e2eTests/BlazorSize Size Reverse.imgstore -------------------------------------------------------------------------------- /BlazorSize.e2eTests/BlazorSize Size Reverse.tstest.cs: -------------------------------------------------------------------------------- 1 | using Telerik.TestStudio.Translators.Common; 2 | using Telerik.TestingFramework.Controls.TelerikUI.Blazor; 3 | using Telerik.TestingFramework.Controls.KendoUI.Angular; 4 | using Telerik.TestingFramework.Controls.KendoUI; 5 | using Telerik.WebAii.Controls.Html; 6 | using Telerik.WebAii.Controls.Xaml; 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | using System.Linq; 11 | 12 | using ArtOfTest.Common.UnitTesting; 13 | using ArtOfTest.WebAii.Core; 14 | using ArtOfTest.WebAii.Controls.HtmlControls; 15 | using ArtOfTest.WebAii.Controls.HtmlControls.HtmlAsserts; 16 | using ArtOfTest.WebAii.Design; 17 | using ArtOfTest.WebAii.Design.Execution; 18 | using ArtOfTest.WebAii.ObjectModel; 19 | using ArtOfTest.WebAii.Silverlight; 20 | using ArtOfTest.WebAii.Silverlight.UI; 21 | 22 | namespace BlazorSize.e2eTests 23 | { 24 | 25 | public class CanIResizeReverse : BaseWebAiiTest 26 | { 27 | #region [ Dynamic Pages Reference ] 28 | 29 | private Pages _pages; 30 | 31 | /// 32 | /// Gets the Pages object that has references 33 | /// to all the elements, frames or regions 34 | /// in this project. 35 | /// 36 | public Pages Pages 37 | { 38 | get 39 | { 40 | if (_pages == null) 41 | { 42 | _pages = new Pages(Manager.Current); 43 | } 44 | return _pages; 45 | } 46 | } 47 | 48 | #endregion 49 | 50 | // Add your test methods here... 51 | 52 | [CodedStep(@"Open Desktop")] 53 | public void OpenWin_Mobile() 54 | { 55 | Actions.InvokeScript(@"openWin(""size"", 560, 800)"); 56 | } 57 | 58 | [CodedStep(@"Resize browser to tablet")] 59 | public void Resize_Tablet() 60 | { 61 | Manager.Browsers[0].Actions.InvokeScript(@"resizeWin(800,800)"); 62 | } 63 | 64 | [CodedStep(@"Resize browser to small")] 65 | public void Resize_Desktop() 66 | { 67 | Manager.Browsers[0].Actions.InvokeScript(@"resizeWin(1920,1080)"); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /BlazorSize.e2eTests/BlazorSize Size Tests.imgstore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EdCharbeneau/BlazorSize/59af9c85463a6a675a91cfc957bd0a81f2dd8209/BlazorSize.e2eTests/BlazorSize Size Tests.imgstore -------------------------------------------------------------------------------- /BlazorSize.e2eTests/BlazorSize Size Tests.tstest.cs: -------------------------------------------------------------------------------- 1 | using Telerik.TestStudio.Translators.Common; 2 | using Telerik.TestingFramework.Controls.TelerikUI.Blazor; 3 | using Telerik.TestingFramework.Controls.KendoUI.Angular; 4 | using Telerik.TestingFramework.Controls.KendoUI; 5 | using Telerik.WebAii.Controls.Html; 6 | using Telerik.WebAii.Controls.Xaml; 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | using System.Linq; 11 | 12 | using ArtOfTest.Common.UnitTesting; 13 | using ArtOfTest.WebAii.Core; 14 | using ArtOfTest.WebAii.Controls.HtmlControls; 15 | using ArtOfTest.WebAii.Controls.HtmlControls.HtmlAsserts; 16 | using ArtOfTest.WebAii.Design; 17 | using ArtOfTest.WebAii.Design.Execution; 18 | using ArtOfTest.WebAii.ObjectModel; 19 | using ArtOfTest.WebAii.Silverlight; 20 | using ArtOfTest.WebAii.Silverlight.UI; 21 | 22 | namespace BlazorSize.e2eTests 23 | { 24 | 25 | public class CanIResize : BaseWebAiiTest 26 | { 27 | #region [ Dynamic Pages Reference ] 28 | 29 | private Pages _pages; 30 | 31 | /// 32 | /// Gets the Pages object that has references 33 | /// to all the elements, frames or regions 34 | /// in this project. 35 | /// 36 | public Pages Pages 37 | { 38 | get 39 | { 40 | if (_pages == null) 41 | { 42 | _pages = new Pages(Manager.Current); 43 | } 44 | return _pages; 45 | } 46 | } 47 | 48 | #endregion 49 | 50 | // Add your test methods here... 51 | 52 | [CodedStep(@"Open Desktop")] 53 | public void OpenWin() 54 | { 55 | Actions.InvokeScript(@"openWin(""size"", 1920, 1080)"); 56 | } 57 | 58 | [CodedStep(@"Resize browser to tablet")] 59 | public void Resize_Tablet() 60 | { 61 | Manager.Browsers[0].Actions.InvokeScript(@"resizeWin(800,800)"); 62 | } 63 | 64 | [CodedStep(@"Resize browser to small")] 65 | public void Resize_Mobile() 66 | { 67 | Manager.Browsers[0].Actions.InvokeScript(@"resizeWin(560,800)"); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /BlazorSize.e2eTests/BlazorSize Test Multicast Reverse.imgstore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EdCharbeneau/BlazorSize/59af9c85463a6a675a91cfc957bd0a81f2dd8209/BlazorSize.e2eTests/BlazorSize Test Multicast Reverse.imgstore -------------------------------------------------------------------------------- /BlazorSize.e2eTests/BlazorSize Test Multicast Reverse.tstest.cs: -------------------------------------------------------------------------------- 1 | using Telerik.TestStudio.Translators.Common; 2 | using Telerik.TestingFramework.Controls.TelerikUI.Blazor; 3 | using Telerik.TestingFramework.Controls.KendoUI.Angular; 4 | using Telerik.TestingFramework.Controls.KendoUI; 5 | using Telerik.WebAii.Controls.Html; 6 | using Telerik.WebAii.Controls.Xaml; 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | using System.Linq; 11 | 12 | using ArtOfTest.Common.UnitTesting; 13 | using ArtOfTest.WebAii.Core; 14 | using ArtOfTest.WebAii.Controls.HtmlControls; 15 | using ArtOfTest.WebAii.Controls.HtmlControls.HtmlAsserts; 16 | using ArtOfTest.WebAii.Design; 17 | using ArtOfTest.WebAii.Design.Execution; 18 | using ArtOfTest.WebAii.ObjectModel; 19 | using ArtOfTest.WebAii.Silverlight; 20 | using ArtOfTest.WebAii.Silverlight.UI; 21 | 22 | namespace BlazorSize.e2eTests 23 | { 24 | 25 | public class BlazorSize_Test_Consolidation_Reverse : BaseWebAiiTest 26 | { 27 | #region [ Dynamic Pages Reference ] 28 | 29 | private Pages _pages; 30 | 31 | /// 32 | /// Gets the Pages object that has references 33 | /// to all the elements, frames or regions 34 | /// in this project. 35 | /// 36 | public Pages Pages 37 | { 38 | get 39 | { 40 | if (_pages == null) 41 | { 42 | _pages = new Pages(Manager.Current); 43 | } 44 | return _pages; 45 | } 46 | } 47 | 48 | #endregion 49 | 50 | // Add your test methods here... 51 | 52 | [CodedStep(@"Open Desktop")] 53 | public void OpenWin_Mobile() 54 | { 55 | Actions.InvokeScript(@"openWin(""multicast"", 560,800)"); 56 | 57 | } 58 | 59 | [CodedStep(@"Resize browser to tablet")] 60 | public void Resize_Tablet() 61 | { 62 | Manager.Browsers[0].Actions.InvokeScript(@"resizeWin(800,800)"); 63 | 64 | } 65 | 66 | [CodedStep(@"Resize browser to small")] 67 | public void Resize_Desktop() 68 | { 69 | Manager.Browsers[0].Actions.InvokeScript(@"resizeWin(1920, 1080)"); 70 | 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /BlazorSize.e2eTests/BlazorSize Test Multicast.imgstore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EdCharbeneau/BlazorSize/59af9c85463a6a675a91cfc957bd0a81f2dd8209/BlazorSize.e2eTests/BlazorSize Test Multicast.imgstore -------------------------------------------------------------------------------- /BlazorSize.e2eTests/BlazorSize Test Multicast.tstest.cs: -------------------------------------------------------------------------------- 1 | using Telerik.TestStudio.Translators.Common; 2 | using Telerik.TestingFramework.Controls.TelerikUI.Blazor; 3 | using Telerik.TestingFramework.Controls.KendoUI.Angular; 4 | using Telerik.TestingFramework.Controls.KendoUI; 5 | using Telerik.WebAii.Controls.Html; 6 | using Telerik.WebAii.Controls.Xaml; 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | using System.Linq; 11 | 12 | using ArtOfTest.Common.UnitTesting; 13 | using ArtOfTest.WebAii.Core; 14 | using ArtOfTest.WebAii.Controls.HtmlControls; 15 | using ArtOfTest.WebAii.Controls.HtmlControls.HtmlAsserts; 16 | using ArtOfTest.WebAii.Design; 17 | using ArtOfTest.WebAii.Design.Execution; 18 | using ArtOfTest.WebAii.ObjectModel; 19 | using ArtOfTest.WebAii.Silverlight; 20 | using ArtOfTest.WebAii.Silverlight.UI; 21 | 22 | namespace BlazorSize.e2eTests 23 | { 24 | 25 | public class BlazorSize_Test_Consolidation : BaseWebAiiTest 26 | { 27 | #region [ Dynamic Pages Reference ] 28 | 29 | private Pages _pages; 30 | 31 | /// 32 | /// Gets the Pages object that has references 33 | /// to all the elements, frames or regions 34 | /// in this project. 35 | /// 36 | public Pages Pages 37 | { 38 | get 39 | { 40 | if (_pages == null) 41 | { 42 | _pages = new Pages(Manager.Current); 43 | } 44 | return _pages; 45 | } 46 | } 47 | 48 | #endregion 49 | 50 | // Add your test methods here... 51 | 52 | [CodedStep(@"Open Desktop")] 53 | public void OpenWin() 54 | { 55 | Actions.InvokeScript(@"openWin(""multicast"", 1920, 1080)"); 56 | 57 | } 58 | 59 | [CodedStep(@"Resize browser to tablet")] 60 | public void Resize_Tablet() 61 | { 62 | Manager.Browsers[0].Actions.InvokeScript(@"resizeWin(800,800)"); 63 | 64 | } 65 | 66 | [CodedStep(@"Resize browser to small")] 67 | public void Resize_Mobile() 68 | { 69 | Manager.Browsers[0].Actions.InvokeScript(@"resizeWin(560,800)"); 70 | 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /BlazorSize.e2eTests/BlazorSize.e2eTests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Debug 4 | AnyCPU 5 | 6 | 7 | 2.0 8 | {0241A973-6232-4FD2-A3F2-11CF65C27FEA} 9 | Library 10 | Properties 11 | BlazorSize.e2eTests 12 | BlazorSize.e2eTests 13 | v4.7.2 14 | 512 15 | {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 16 | 17 | 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | 34 | 35 | 36 | 37 | 38 | 4.5 39 | 40 | 41 | 4.5 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | False 53 | 54 | 55 | 56 | 57 | False 58 | 59 | 60 | 61 | 62 | BlazorSize Size Tests.tstest 63 | 64 | 65 | 66 | 67 | BlazorSize Construct & Dispose.tstest 68 | 69 | 70 | 71 | BlazorSize Size Tests.tstest 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | BlazorSize Construct & Dispose.tstest 82 | 83 | 84 | BlazorSize Size Tests.tstest 85 | 86 | 87 | 88 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /BlazorSize.e2eTests/Profiler Configurations/{1c6f74bd-1a14-4014-a8c9-02fdb3cb8302}.tsprofconfig: -------------------------------------------------------------------------------- 1 | { 2 | "__type": "ArtOfTest.WebAii.Design.Execution.Profiler.ProfilerMetadataConfigurationSet", 3 | "__value": { 4 | "_bFirstTimeUserConfigurationComplete": false, 5 | "Metadata": { 6 | "__type": "ArtOfTest.WebAii.Design.Execution.Profiler.ProfilerResultsMetadata", 7 | "__value": { 8 | "_resultsId": "00000000-0000-0000-0000-000000000000", 9 | "_ownerTestId": "00000000-0000-0000-0000-000000000000", 10 | "_startTime": "0001-01-01T00:00:00", 11 | "_totalTime": "00:00:00", 12 | "_bCollectProfilerSampleData": false, 13 | "_strDescription": null, 14 | "_strFolder": "C:\\Users\\edcha\\AppData\\Local\\Temp\\", 15 | "_strFilePath": null, 16 | "_browserType": 1, 17 | "_browserVersion": null, 18 | "_bClearBrowserCacheBeforeRun": false, 19 | "_bWarmUpServerBeforeRun": false 20 | } 21 | }, 22 | "ClientPluginConfigurations": {}, 23 | "Servers": {}, 24 | "Version": "2020.3.1118.0" 25 | } 26 | } -------------------------------------------------------------------------------- /BlazorSize.e2eTests/Profiler Configurations/{d3bce0f5-b6f6-45aa-959a-5c1c1753902b}.tsprofconfig: -------------------------------------------------------------------------------- 1 | { 2 | "__type": "ArtOfTest.WebAii.Design.Execution.Profiler.ProfilerMetadataConfigurationSet", 3 | "__value": { 4 | "_bFirstTimeUserConfigurationComplete": false, 5 | "Metadata": { 6 | "__type": "ArtOfTest.WebAii.Design.Execution.Profiler.ProfilerResultsMetadata", 7 | "__value": { 8 | "_resultsId": "00000000-0000-0000-0000-000000000000", 9 | "_ownerTestId": "00000000-0000-0000-0000-000000000000", 10 | "_startTime": "0001-01-01T00:00:00", 11 | "_totalTime": "00:00:00", 12 | "_bCollectProfilerSampleData": false, 13 | "_strDescription": null, 14 | "_strFolder": "C:\\Users\\edcha\\AppData\\Local\\Temp\\", 15 | "_strFilePath": null, 16 | "_browserType": 1, 17 | "_browserVersion": null, 18 | "_bClearBrowserCacheBeforeRun": false, 19 | "_bWarmUpServerBeforeRun": false 20 | } 21 | }, 22 | "ClientPluginConfigurations": {}, 23 | "Servers": {}, 24 | "Version": "2020.3.1118.0" 25 | } 26 | } -------------------------------------------------------------------------------- /BlazorSize.e2eTests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("BlazorSize.e2eTests")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("BlazorSize.e2eTests")] 13 | [assembly: AssemblyCopyright("Copyright © 2021")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("0241a973-6232-4fd2-a3f2-11cf65c27fea")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | [assembly: AssemblyVersion("1.0.0.0")] 35 | [assembly: AssemblyFileVersion("1.0.0.0")] 36 | -------------------------------------------------------------------------------- /BlazorSize.e2eTests/Settings.aiis: -------------------------------------------------------------------------------- 1 | { 2 | "__type": "ArtOfTest.WebAii.Design.UserSettings", 3 | "__value": { 4 | "UseHttpProxy": false, 5 | "RecordjQueryInDescriptorsIfPossible": true, 6 | "ShouldDeleteElement": false, 7 | "SkipDeleteElementPrompt": false, 8 | "HideFindExpressionWelcome": false, 9 | "MenuHoldTime": 1.0, 10 | "SilverlightConnectTimeout": 60000, 11 | "BaseClassName": "BaseWebAiiTest", 12 | "UrlRecordMode": 2, 13 | "HighlightBorderColor": -65536, 14 | "HighlightBorderSize": 1, 15 | "CodeGenerationElementIdentificationType": 1, 16 | "UrlHistory": [], 17 | "AbsoluteDragDropRecording": true, 18 | "ShowStoryboardHorizontalLayout": false, 19 | "ImageScalePercentage": 75, 20 | "SkipTranslatorsOptimization": false, 21 | "SelectedIdentificaitonScheme": "Html", 22 | "IdentificationSchemes": { 23 | "Html": { 24 | "__type": "ArtOfTest.Common.Design.Translation.IdentificationOptionsScheme", 25 | "__value": { 26 | "CheckFindParamUniqueness": true, 27 | "AutoDetectTestRegions": true, 28 | "TryAttributeCombinations": true, 29 | "AlwaysAssertTagName": true, 30 | "IdentificationsPerTag": { 31 | "All Elements": { 32 | "__type": "ArtOfTest.Common.Design.Translation.IdentificationDescriptorList", 33 | "__value": [ 34 | { 35 | "__type": "ArtOfTest.WebAii.Design.Translation.HtmlIdentificationDescriptor", 36 | "__value": { 37 | "IsLocked": false, 38 | "SearchType": 0, 39 | "AttributeName": "id" 40 | } 41 | }, 42 | { 43 | "__type": "ArtOfTest.WebAii.Design.Translation.HtmlIdentificationDescriptor", 44 | "__value": { 45 | "IsLocked": false, 46 | "SearchType": 0, 47 | "AttributeName": "name" 48 | } 49 | }, 50 | { 51 | "__type": "ArtOfTest.WebAii.Design.Translation.HtmlIdentificationDescriptor", 52 | "__value": { 53 | "IsLocked": false, 54 | "SearchType": 0, 55 | "AttributeName": "src" 56 | } 57 | }, 58 | { 59 | "__type": "ArtOfTest.WebAii.Design.Translation.HtmlIdentificationDescriptor", 60 | "__value": { 61 | "IsLocked": false, 62 | "SearchType": 0, 63 | "AttributeName": "href" 64 | } 65 | }, 66 | { 67 | "__type": "ArtOfTest.WebAii.Design.Translation.HtmlIdentificationDescriptor", 68 | "__value": { 69 | "IsLocked": false, 70 | "SearchType": 0, 71 | "AttributeName": "value" 72 | } 73 | }, 74 | { 75 | "__type": "ArtOfTest.WebAii.Design.Translation.HtmlIdentificationDescriptor", 76 | "__value": { 77 | "IsLocked": false, 78 | "SearchType": 0, 79 | "AttributeName": "alt" 80 | } 81 | }, 82 | { 83 | "__type": "ArtOfTest.WebAii.Design.Translation.HtmlIdentificationDescriptor", 84 | "__value": { 85 | "IsLocked": true, 86 | "SearchType": 1, 87 | "AttributeName": null 88 | } 89 | }, 90 | { 91 | "__type": "ArtOfTest.WebAii.Design.Translation.HtmlIdentificationDescriptor", 92 | "__value": { 93 | "IsLocked": true, 94 | "SearchType": 8, 95 | "AttributeName": null 96 | } 97 | } 98 | ] 99 | } 100 | }, 101 | "TechnologyType": 1 102 | } 103 | }, 104 | "Silverlight": { 105 | "__type": "ArtOfTest.Common.Design.Translation.IdentificationOptionsScheme", 106 | "__value": { 107 | "CheckFindParamUniqueness": true, 108 | "AutoDetectTestRegions": true, 109 | "TryAttributeCombinations": true, 110 | "AlwaysAssertTagName": true, 111 | "IdentificationsPerTag": { 112 | "All Elements": { 113 | "__type": "ArtOfTest.Common.Design.Translation.IdentificationDescriptorList", 114 | "__value": [ 115 | { 116 | "__type": "ArtOfTest.WebAii.Design.Translation.Silverlight.SilverlightIdentificationDescriptor", 117 | "__value": { 118 | "IsLocked": false, 119 | "SearchType": 0, 120 | "AttributeName": null 121 | } 122 | }, 123 | { 124 | "__type": "ArtOfTest.WebAii.Design.Translation.Silverlight.SilverlightIdentificationDescriptor", 125 | "__value": { 126 | "IsLocked": false, 127 | "SearchType": 5, 128 | "AttributeName": null 129 | } 130 | }, 131 | { 132 | "__type": "ArtOfTest.WebAii.Design.Translation.Silverlight.SilverlightIdentificationDescriptor", 133 | "__value": { 134 | "IsLocked": false, 135 | "SearchType": 1, 136 | "AttributeName": null 137 | } 138 | }, 139 | { 140 | "__type": "ArtOfTest.WebAii.Design.Translation.Silverlight.SilverlightIdentificationDescriptor", 141 | "__value": { 142 | "IsLocked": true, 143 | "SearchType": 6, 144 | "AttributeName": null 145 | } 146 | } 147 | ] 148 | } 149 | }, 150 | "TechnologyType": 2 151 | } 152 | }, 153 | "Desktop": { 154 | "__type": "ArtOfTest.Common.Design.Translation.IdentificationOptionsScheme", 155 | "__value": { 156 | "CheckFindParamUniqueness": true, 157 | "AutoDetectTestRegions": true, 158 | "TryAttributeCombinations": true, 159 | "AlwaysAssertTagName": true, 160 | "IdentificationsPerTag": { 161 | "All Elements": { 162 | "__type": "ArtOfTest.Common.Design.Translation.IdentificationDescriptorList", 163 | "__value": [ 164 | { 165 | "__type": "ArtOfTest.WebAii.Design.Translation.Desktop.DesktopIdentificationDescriptor", 166 | "__value": { 167 | "PropertyName": "ControlTypeName", 168 | "IsLocked": true 169 | } 170 | } 171 | ] 172 | } 173 | }, 174 | "TechnologyType": 8 175 | } 176 | } 177 | }, 178 | "UnitTypeTypeGeneration": 1, 179 | "RecorderBaseUrl": "https://localhost:7249", 180 | "IsStoryBoardCapturingEnabled": true, 181 | "IsElementImageCapturingEnabled": true, 182 | "UseScreenshotCache": true, 183 | "EnableImageSearch": true, 184 | "UseBrowserExtension": false, 185 | "ChromiumFirstInteractionDelay": 500, 186 | "FirefoxMinimumJSClickDelay": 100, 187 | "TelerikComponentsVersion": 10000, 188 | "ScrollOnImageSearch": true, 189 | "DisplayElementImagePreview": true, 190 | "ElementImageThreshold": 90, 191 | "ElementImageSearchTimeout": 15000, 192 | "SearchByImageFirst": false, 193 | "ElementImageSearchDelay": 300, 194 | "SimulateRealClickByDefault": true, 195 | "SimulateRealTypingByDefault": true, 196 | "DefaultDropDownSelection": 1, 197 | "QuickExecutionElementWaitTimeout": 15000, 198 | "QuickExecutionClientReadyTimeout": 60000, 199 | "TfsUserName": "", 200 | "TfsPassword": "", 201 | "TfsSkipAuthDialog": true, 202 | "TfsDomain": "", 203 | "RecordWpfWindowStateChanged": false, 204 | "PromptNameOnAddElement": true, 205 | "DefaultWPFApplication": null, 206 | "DefaultWPFApplicationArgs": null, 207 | "DefaultDesktopApplication": null, 208 | "DefaultDesktopApplicationArgs": null, 209 | "UseLegacySilverlightFindLogic": false, 210 | "ProjectGuid": "1f8b050d-e725-4ceb-b9a0-6eab12b5769b", 211 | "SourceControlRepository": null, 212 | "IsOnline": false, 213 | "ProjectLanguage": 1, 214 | "ProjectReferences": [ 215 | "System", 216 | "System.Core", 217 | "ArtOfTest.WebAii, Version=2024.1.409.1, Culture=neutral, PublicKeyToken=4fd5f65be123776c", 218 | "ArtOfTest.WebAii.Messaging, Version=2024.1.409.1, Culture=neutral, PublicKeyToken=4fd5f65be123776c", 219 | "ArtOfTest.WebAii.Design, Version=2024.1.409.1, Culture=neutral, PublicKeyToken=4fc62bbc3827ab1d", 220 | "Telerik.WebAii.Controls.Html, Version=2024.1.409.1, Culture=neutral, PublicKeyToken=528163f3e645de45", 221 | "Telerik.WebAii.Controls.Xaml, Version=2024.1.409.1, Culture=neutral, PublicKeyToken=528163f3e645de45", 222 | "Telerik.WebAii.Controls.Xaml.Wpf, Version=2024.1.409.1, Culture=neutral, PublicKeyToken=528163f3e645de45", 223 | "Telerik.TestingFramework.Controls.KendoUI, Version=2024.1.409.1, Culture=neutral, PublicKeyToken=528163f3e645de45", 224 | "Telerik.TestingFramework.Controls.KendoUI.Angular, Version=2024.1.409.1, Culture=neutral, PublicKeyToken=528163f3e645de45", 225 | "Telerik.TestingFramework.Controls.TelerikUI.Blazor, Version=2024.1.409.1, Culture=neutral, PublicKeyToken=528163f3e645de45", 226 | "Telerik.TestStudio.Translators.Common, Version=2024.1.409.1, Culture=neutral, PublicKeyToken=528163f3e645de45", 227 | "System.Windows.Forms" 228 | ], 229 | "ScheduleServerUrl": "http://localhost:8009", 230 | "IsScheduleServerRemote": false, 231 | "NotificationSettings": null, 232 | "WebComponents": true, 233 | "ProjectVersion": "2024.1.207.0", 234 | "Namespace": "BlazorSize.e2eTests", 235 | "AssemblyName": "BlazorSize.e2eTests", 236 | "OutputFolder": "bin", 237 | "ExcludedFiles": [], 238 | "InDevelopmentFiles": [], 239 | "DisabledTranslators": [], 240 | "SkipFindExpressionSetDataDrivenWarning": false, 241 | "BugTrackerPersistableSettings": {}, 242 | "ActiveBugTrackers": [], 243 | "BugTitleMask": "{Step name} step on {Test name} test failed.", 244 | "BugAddAttachment": true, 245 | "BugAutoSubmit": false, 246 | "BugDescriptionMask": "{Description}", 247 | "SelectedBrowserOption": 7, 248 | "SelectedResponsiveBrowserOption": 7, 249 | "ResponsiveBrowserDevice": "iPad", 250 | "ResponsiveBrowserWidth": 768, 251 | "ResponsiveBrowserHeight": 1024, 252 | "ResponsiveBrowserUserAgent": "Mozilla/5.0 (iPad; CPU OS 11_0 like Mac OS X) AppleWebKit/604.1.34 (KHTML, like Gecko) Version/11.0 Mobile/15A5341f Safari/604.1", 253 | "ActiveTheme": "Light", 254 | "ShowStepResultDetails": false 255 | } 256 | } -------------------------------------------------------------------------------- /BlazorSize.e2eTests/TestLists/BlazorSize .NET Web App.aiilist: -------------------------------------------------------------------------------- 1 | { 2 | "__type": "ArtOfTest.WebAii.Design.Execution.TestList", 3 | "__value": { 4 | "TestListId": "f9449235-bf5e-4504-8109-14a9a1491f56", 5 | "Id": "f9449235-bf5e-4504-8109-14a9a1491f56", 6 | "TestListName": "BlazorSize .NET Web App", 7 | "CreationDate": "2021-02-09T13:21:46-05:00", 8 | "OwnerName": null, 9 | "Settings": { 10 | "__type": "ArtOfTest.WebAii.Core.Settings", 11 | "__value": { 12 | "ResponsiveWeb": { 13 | "__type": "ArtOfTest.WebAii.Core.Settings+ResponsiveWebSettings", 14 | "__value": { 15 | "Width": 768, 16 | "Height": 1024, 17 | "UserAgent": "Mozilla/5.0 (iPad; CPU OS 11_0 like Mac OS X) AppleWebKit/604.1.34 (KHTML, like Gecko) Version/11.0 Mobile/15A5341f Safari/604.1" 18 | } 19 | }, 20 | "Web": { 21 | "__type": "ArtOfTest.WebAii.Core.Settings+WebSettings", 22 | "__value": { 23 | "IsProfilingExecution": false, 24 | "ExecutingBrowsers": [], 25 | "UseMultiBrowserExecution": false, 26 | "RecycleBrowser": false, 27 | "AspNetDevServerPort": -1, 28 | "LocalWebServer": 0, 29 | "EnableUILessRequestViewing": false, 30 | "WebAppPhysicalPath": "", 31 | "DefaultBrowser": 7, 32 | "EnableScriptLogging": false, 33 | "BaseUrl": "https://localhost:7249/", 34 | "KillBrowserProcessOnClose": false, 35 | "KillBrowsersBeforeStart": true, 36 | "AutoCalibrateBrowsers": false, 37 | "UseHttpProxy": false, 38 | "EnableSilverlight": false, 39 | "VerboseHttpProxy": false, 40 | "SilverlightConnectTimeout": 60000, 41 | "SilverlightApplicationPath": null, 42 | "WebComponents": true, 43 | "UseBrowserExtension": false, 44 | "ChromiumFirstInteractionDelay": 500, 45 | "FirefoxMinimumJSClickDelay": 100 46 | } 47 | }, 48 | "Wpf": { 49 | "__type": "ArtOfTest.WebAii.Core.Settings+WpfSettings", 50 | "__value": { 51 | "DefaultApplicationPath": null, 52 | "DefaultApplicationArgs": "" 53 | } 54 | }, 55 | "Desktop": { 56 | "__type": "ArtOfTest.WebAii.Core.Settings+DesktopSettings", 57 | "__value": { 58 | "DefaultApplicationPath": "", 59 | "DefaultApplicationArgs": "" 60 | } 61 | }, 62 | "CreateLogFile": true, 63 | "LogLocation": "C:\\WebAiiLog\\", 64 | "QueryEventLogErrorsOnExit": false, 65 | "LogAnnotations": false, 66 | "SimulatedMouseMoveSpeed": 0.3, 67 | "ExecuteInDevelopmentTests": false, 68 | "WaitCheckInterval": 500, 69 | "ElementWaitTimeout": 15000, 70 | "SearchByImageFirst": false, 71 | "ExecuteCommandTimeout": 20000, 72 | "ExecutionDelay": 0, 73 | "RerunFailedTests": false, 74 | "UnexpectedDialogAction": 2, 75 | "AnnotateExecution": false, 76 | "AnnotationMode": 0, 77 | "RecordingMode": 0, 78 | "RecordingCodec": 0, 79 | "RecordingScale": 100, 80 | "RecordingFPS": 5, 81 | "RecordingOutputLocation": "", 82 | "RecordingSizeLimit": 0, 83 | "XMultiMgr": true, 84 | "ClientReadyTimeout": 60000, 85 | "DisableDialogMonitoring": false, 86 | "RunnerResponseTimeout": 0.0, 87 | "EnableImageSearch": true, 88 | "ElementImageSearchTimeout": 15000, 89 | "ElementImageSearchDelay": 300, 90 | "ScrollOnImageSearch": true, 91 | "TelerikComponentsVersion": 10000 92 | } 93 | }, 94 | "Tests": [], 95 | "Filter": { 96 | "__type": "ArtOfTest.WebAii.Design.Execution.Filter", 97 | "__value": { 98 | "Keys": [ 99 | "Name" 100 | ], 101 | "Comparisons": [ 102 | "ArtOfTest.WebAii.Design.Execution.FilterComparison||StartsWith" 103 | ], 104 | "Values": [ 105 | "BlazorSize" 106 | ] 107 | } 108 | }, 109 | "ListType": 0, 110 | "ProjectId": "1f8b050d-e725-4ceb-b9a0-6eab12b5769b" 111 | } 112 | } -------------------------------------------------------------------------------- /BlazorSize.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.0.31521.260 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlazorPro.BlazorSize", "BlazorSize\BlazorPro.BlazorSize.csproj", "{2CB53507-42FE-44BE-B5A4-61480966D2A9}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestComponents", "TestComponents\TestComponents.csproj", "{E717846B-D7B7-4C0E-AFEE-6E194086EE78}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "e2e", "e2e", "{FC14BECA-41E0-4D03-9A64-E1CF65AAD2C9}" 11 | EndProject 12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "bUnitCompatibilityTests", "bUnitCompatibilityTests\bUnitCompatibilityTests.csproj", "{23B56776-2EE1-474A-9FD8-B1E1974D068C}" 13 | EndProject 14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlazorPro.BlazorSize.UnitTesting", "BlazorSize.UnitTesting\BlazorPro.BlazorSize.UnitTesting.csproj", "{A1361B27-57E2-4A85-AD32-0F6B1CA0C034}" 15 | EndProject 16 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlazorPro.BlazorSize.BlazorRepl", "BlazorPro.BlazorSize.BlazorRepl\BlazorPro.BlazorSize.BlazorRepl.csproj", "{EFDE2359-ED21-4D36-B069-674369695FD9}" 17 | EndProject 18 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BlazorWebApp", "BlazorWebApp\BlazorWebApp\BlazorWebApp.csproj", "{0FEB608D-5C88-4E9E-A374-6241F1BA1BA6}" 19 | EndProject 20 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BlazorWebApp.Client", "BlazorWebApp\BlazorWebApp.Client\BlazorWebApp.Client.csproj", "{7A2D510A-39DF-4730-9CE4-6D0E9E6B17BB}" 21 | EndProject 22 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "REPL Support", "REPL Support", "{4EED29C4-82C3-499B-8D76-7D62880C1CE7}" 23 | EndProject 24 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Unit Testing", "Unit Testing", "{DB04D97C-9287-4612-B6BF-CC70B8F8B3BE}" 25 | EndProject 26 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PlaywrightTests", "PlaywrightTests\PlaywrightTests.csproj", "{4654682C-9371-48B2-A078-CD21C560D0D4}" 27 | EndProject 28 | Global 29 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 30 | Debug|Any CPU = Debug|Any CPU 31 | Release|Any CPU = Release|Any CPU 32 | EndGlobalSection 33 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 34 | {2CB53507-42FE-44BE-B5A4-61480966D2A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 35 | {2CB53507-42FE-44BE-B5A4-61480966D2A9}.Debug|Any CPU.Build.0 = Debug|Any CPU 36 | {2CB53507-42FE-44BE-B5A4-61480966D2A9}.Release|Any CPU.ActiveCfg = Release|Any CPU 37 | {2CB53507-42FE-44BE-B5A4-61480966D2A9}.Release|Any CPU.Build.0 = Release|Any CPU 38 | {E717846B-D7B7-4C0E-AFEE-6E194086EE78}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 39 | {E717846B-D7B7-4C0E-AFEE-6E194086EE78}.Debug|Any CPU.Build.0 = Debug|Any CPU 40 | {E717846B-D7B7-4C0E-AFEE-6E194086EE78}.Release|Any CPU.ActiveCfg = Release|Any CPU 41 | {E717846B-D7B7-4C0E-AFEE-6E194086EE78}.Release|Any CPU.Build.0 = Release|Any CPU 42 | {23B56776-2EE1-474A-9FD8-B1E1974D068C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 43 | {23B56776-2EE1-474A-9FD8-B1E1974D068C}.Debug|Any CPU.Build.0 = Debug|Any CPU 44 | {23B56776-2EE1-474A-9FD8-B1E1974D068C}.Release|Any CPU.ActiveCfg = Release|Any CPU 45 | {23B56776-2EE1-474A-9FD8-B1E1974D068C}.Release|Any CPU.Build.0 = Release|Any CPU 46 | {A1361B27-57E2-4A85-AD32-0F6B1CA0C034}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 47 | {A1361B27-57E2-4A85-AD32-0F6B1CA0C034}.Debug|Any CPU.Build.0 = Debug|Any CPU 48 | {A1361B27-57E2-4A85-AD32-0F6B1CA0C034}.Release|Any CPU.ActiveCfg = Release|Any CPU 49 | {A1361B27-57E2-4A85-AD32-0F6B1CA0C034}.Release|Any CPU.Build.0 = Release|Any CPU 50 | {EFDE2359-ED21-4D36-B069-674369695FD9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 51 | {EFDE2359-ED21-4D36-B069-674369695FD9}.Debug|Any CPU.Build.0 = Debug|Any CPU 52 | {EFDE2359-ED21-4D36-B069-674369695FD9}.Release|Any CPU.ActiveCfg = Release|Any CPU 53 | {EFDE2359-ED21-4D36-B069-674369695FD9}.Release|Any CPU.Build.0 = Release|Any CPU 54 | {0FEB608D-5C88-4E9E-A374-6241F1BA1BA6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 55 | {0FEB608D-5C88-4E9E-A374-6241F1BA1BA6}.Debug|Any CPU.Build.0 = Debug|Any CPU 56 | {0FEB608D-5C88-4E9E-A374-6241F1BA1BA6}.Release|Any CPU.ActiveCfg = Release|Any CPU 57 | {0FEB608D-5C88-4E9E-A374-6241F1BA1BA6}.Release|Any CPU.Build.0 = Release|Any CPU 58 | {7A2D510A-39DF-4730-9CE4-6D0E9E6B17BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 59 | {7A2D510A-39DF-4730-9CE4-6D0E9E6B17BB}.Debug|Any CPU.Build.0 = Debug|Any CPU 60 | {7A2D510A-39DF-4730-9CE4-6D0E9E6B17BB}.Release|Any CPU.ActiveCfg = Release|Any CPU 61 | {7A2D510A-39DF-4730-9CE4-6D0E9E6B17BB}.Release|Any CPU.Build.0 = Release|Any CPU 62 | {4654682C-9371-48B2-A078-CD21C560D0D4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 63 | {4654682C-9371-48B2-A078-CD21C560D0D4}.Debug|Any CPU.Build.0 = Debug|Any CPU 64 | {4654682C-9371-48B2-A078-CD21C560D0D4}.Release|Any CPU.ActiveCfg = Release|Any CPU 65 | {4654682C-9371-48B2-A078-CD21C560D0D4}.Release|Any CPU.Build.0 = Release|Any CPU 66 | EndGlobalSection 67 | GlobalSection(SolutionProperties) = preSolution 68 | HideSolutionNode = FALSE 69 | EndGlobalSection 70 | GlobalSection(NestedProjects) = preSolution 71 | {E717846B-D7B7-4C0E-AFEE-6E194086EE78} = {FC14BECA-41E0-4D03-9A64-E1CF65AAD2C9} 72 | {23B56776-2EE1-474A-9FD8-B1E1974D068C} = {DB04D97C-9287-4612-B6BF-CC70B8F8B3BE} 73 | {A1361B27-57E2-4A85-AD32-0F6B1CA0C034} = {DB04D97C-9287-4612-B6BF-CC70B8F8B3BE} 74 | {EFDE2359-ED21-4D36-B069-674369695FD9} = {4EED29C4-82C3-499B-8D76-7D62880C1CE7} 75 | {0FEB608D-5C88-4E9E-A374-6241F1BA1BA6} = {FC14BECA-41E0-4D03-9A64-E1CF65AAD2C9} 76 | {7A2D510A-39DF-4730-9CE4-6D0E9E6B17BB} = {FC14BECA-41E0-4D03-9A64-E1CF65AAD2C9} 77 | {4654682C-9371-48B2-A078-CD21C560D0D4} = {FC14BECA-41E0-4D03-9A64-E1CF65AAD2C9} 78 | EndGlobalSection 79 | GlobalSection(ExtensibilityGlobals) = postSolution 80 | SolutionGuid = {87B9FF13-915E-4E9D-B6F0-413C9B2AA3F8} 81 | EndGlobalSection 82 | EndGlobal 83 | -------------------------------------------------------------------------------- /BlazorSize/BlazorPro.BlazorSize.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net9.0 5 | true 6 | enable 7 | latest 8 | 9.0.0 9 | Ed Charbeneau 10 | EdCharbeneau.com 11 | A JavaScript interop for Blazor used to detect the browser's screen size and perform Media Query tests. BlazorSize uses the DOM API `matchMedia` to test screen size. BlazorSize was created to allow Blazor components to render adaptively. 12 | LICENSE.txt 13 | https://github.com/EdCharbeneau/BlazorSize 14 | readme.md 15 | blazor-glove-128.jpg 16 | 17 | https://github.com/EdCharbeneau/BlazorSize 18 | Blazor, JavaScript Interop 19 | 20 | 9.0.0 Fixes issue with UnobservedTaskExceptions 21 | 8.0.0 Adds support for .NET 8 interactive modes 22 | 6.2.2 Fixes more codepaths that may result in Microsoft.JSInterop.JSDisconnectedException 23 | 6.2.1 Fixed Microsoft.JSInterop.JSDisconnectedException 24 | 6.2.0 Fixed bug where MediaQuery dispose was not always working correctly. 25 | 6.1.2 Fixed casing on JS modules, fixes deployment issues on Linux. (regression) 26 | 6.1.1 Replaced deprecated `addListener` method. Removed console log messages, again. 27 | 6.1.0 Removed dependency for bUnit.Core. Breaking changes may occur in test projects. Removed console log messages. 28 | 6.0.0 Fixed possible null issues. .NET 6 support. Moved APIs for bUnit to BlazorPro.BlazorSize.UnitTesting. 29 | 5.0.0 Added support for bUnit. Removed support for .NET 3.x 30 | 4.0.0 no release, aligning with .NET 5.0. Next version will no longer support .NET 3.x 31 | 3.2.0 32 | Added service interfaces, IMediaQueryService, IResizeListener to support unit testing scenarios 33 | Added end-to-end testing on source for QA 34 | 3.1.0 35 | Added .NET 5 module loading for BlazorSize Resize Listener. 36 | Fixed casing on JS modules, fixes deployment issues on Linux. 37 | 3.0.0 38 | Added .NET 5 module loading for BlazorSize Media Queries. 39 | 2.3.0 40 | Fixed additional regressions from 2.2.0, update recommended. 41 | 2.2.0 42 | Fixed possible regressions, update recommended. 43 | Updated Blazor dependencies. 44 | 2.1.1 45 | Fixed MSBuild including *.json files breaking NuGet pkg. 46 | 2.0.0 47 | Added MediaQueryList & MediaQuery components. 48 | 1.2.2 49 | Fixes initalization issues with Client-Side Blazor 50 | 1.2.0 51 | Full-release 52 | 53 | 6.2.0.0 54 | 6.2.0.0 55 | BlazorPro.BlazorSize 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | True 71 | 72 | 73 | 74 | 75 | True 76 | 77 | 78 | 79 | 80 | 81 | False 82 | 83 | 84 | Never 85 | False 86 | 87 | 88 | 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /BlazorSize/Configuration/ResizeOptions.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorPro.BlazorSize 2 | { 3 | public class ResizeOptions 4 | { 5 | /// 6 | /// Rate in milliseconds that the browsers `resize()` event should report a change. 7 | /// Setting this value too low can cause poor application performance. 8 | /// 9 | public int ReportRate { get; set; } = 300; 10 | 11 | /// 12 | /// Report resize events and media queries in the browser's console. 13 | /// 14 | public bool EnableLogging { get; set; } = false; 15 | 16 | /// 17 | /// Suppress the first OnResized that is invoked when a new event handler is added. 18 | /// 19 | public bool SuppressInitEvent { get; set; } = false; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /BlazorSize/Configuration/ServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using System; 3 | 4 | namespace BlazorPro.BlazorSize 5 | { 6 | public static class ServiceCollectionExtensions 7 | { 8 | /// 9 | /// Adds a BlazorSize.ResizeListener as a Scoped instance. 10 | /// 11 | /// Defines settings for this instance. 12 | /// Continues the IServiceCollection chain. 13 | public static IServiceCollection AddResizeListener(this IServiceCollection services, 14 | Action? resizeOptions = null) 15 | { 16 | services.AddScoped(); 17 | 18 | if(resizeOptions is not null) 19 | services.Configure(resizeOptions); 20 | 21 | return services; 22 | } 23 | 24 | /// 25 | /// Adds a BlazorSize.MediaQueryService as a Scoped instance. 26 | /// 27 | /// Continues the IServiceCollection chain. 28 | public static IServiceCollection AddMediaQueryService(this IServiceCollection services) 29 | { 30 | services.AddScoped(); 31 | return services; 32 | } 33 | 34 | } 35 | } -------------------------------------------------------------------------------- /BlazorSize/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2019 Ed Charbeneau 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at 4 | 5 | http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. -------------------------------------------------------------------------------- /BlazorSize/MediaQuery/Breakpoints.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorPro.BlazorSize 2 | { 3 | public static class Breakpoints 4 | { 5 | /// @media(min-width: 576px) { ... } 6 | /// Small devices (landscape phones, 576px and up) 7 | public const string SmallUp = "(min-width: 576px)"; 8 | 9 | /// @media(min-width: 768px) { ... } 10 | /// Medium devices (tablets, 768px and up) 11 | public const string MediumUp = "(min-width: 768px)"; 12 | 13 | // Large devices (desktops, 992px and up) 14 | /// @media(min-width: 992px) { ... } 15 | public const string LargeUp = "(min-width: 992px)"; 16 | 17 | /// Extra large devices (large desktops, 1200px and up) 18 | /// @media(min-width: 1200px) { ... } 19 | public const string XLargeUp = "(min-width: 1200px)"; 20 | 21 | /// Extra small devices (portrait phones, less than 576px) 22 | /// @media(max-width: 575.98px) { ... } 23 | public const string XSmallDown = "(max-width: 575.98px)"; 24 | 25 | /// Small devices (landscape phones, less than 768px) 26 | /// @media(max-width: 767.98px) { ... } 27 | public const string SmallDown = "(max-width: 767.98px)"; 28 | 29 | /// Medium devices (tablets, less than 992px) 30 | /// @media(max-width: 991.98px) { ... } 31 | public const string MediumDown = "(max-width: 991.98px)"; 32 | 33 | /// Large devices (desktops, less than 1200px) 34 | /// @media(max-width: 1199.98px) { ... } 35 | public const string LargeDown = "(max-width: 1199.98px)"; 36 | 37 | 38 | /// Small devices (landscape phones, 576px and up) 39 | /// @media(min-width: 576px) and(max-width: 767.98px) { ... } 40 | public static string OnlySmall => Between(SmallDown, SmallUp); 41 | 42 | /// Medium devices (tablets, 768px and up) 43 | /// @media(min-width: 768px) and(max-width: 991.98px) { ... } 44 | public static string OnlyMedium => Between(MediumDown, MediumUp); 45 | 46 | /// Large devices (desktops, 992px and up) 47 | /// @media(min-width: 992px) and(max-width: 1199.98px) { ... } 48 | public static string OnlyLarge => Between(LargeDown, LargeUp); 49 | 50 | /// 51 | /// Combines two media queries with the `and` keyword. 52 | /// Values must include parenthesis. 53 | /// Ex: (max-width: 1199.98px) and (min-width: 992px) 54 | /// 55 | /// 56 | /// 57 | public static string Between(string max, string min) => $"{max} and {min}"; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /BlazorSize/MediaQuery/IMediaQueryService.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.JSInterop; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | 5 | namespace BlazorPro.BlazorSize 6 | { 7 | public interface IMediaQueryService 8 | { 9 | List MediaQueries { get; } 10 | 11 | void AddQuery(MediaQuery newMediaQuery); 12 | Task CreateMediaQueryList(DotNetObjectReference dotNetObjectReference); 13 | 14 | Task Initialize(MediaQuery mediaQuery); 15 | Task RemoveQuery(MediaQuery mediaQuery); 16 | 17 | } 18 | } -------------------------------------------------------------------------------- /BlazorSize/MediaQuery/MediaQuery.razor: -------------------------------------------------------------------------------- 1 | @namespace BlazorPro.BlazorSize 2 | @if (InternalMedia.Matches) 3 | { 4 | @Matched 5 | } 6 | else 7 | { 8 | @Unmatched 9 | } -------------------------------------------------------------------------------- /BlazorSize/MediaQuery/MediaQuery.razor.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Components; 2 | using Microsoft.JSInterop; 3 | using System; 4 | using System.Threading.Tasks; 5 | 6 | namespace BlazorPro.BlazorSize 7 | { 8 | public partial class MediaQuery : IAsyncDisposable 9 | { 10 | /// 11 | /// Media Query string, using standard media query syntax. 12 | /// Ex: "(max-width: 767.98px)" 13 | /// 14 | [Parameter] public string Media { get; set; } = ""; 15 | 16 | /// 17 | /// The result of the specified Media property. 18 | /// Use the @bind-Media directive for automated binding. 19 | /// 20 | [Parameter] public bool Matches { get; set; } 21 | 22 | /// 23 | /// Handles the changed event when the media query's Matches property is changed. 24 | /// Use the @bind-Media directive for automated binding. 25 | /// 26 | [Parameter] public EventCallback MatchesChanged { get; set; } 27 | 28 | /// 29 | /// Optional template, shown only when the Matches value is true. 30 | /// Using a template will disable @bind-Matches for performance. 31 | /// 32 | [Parameter] public RenderFragment? Matched { get; set; } 33 | 34 | /// 35 | /// Optional template, shown only when the Matches value is false. 36 | /// Using a template will disable @bind-Matches for performance. 37 | /// 38 | [Parameter] public RenderFragment? Unmatched { get; set; } 39 | 40 | /// 41 | /// Gets the parent MediaQueryList container. 42 | /// 43 | [CascadingParameter] public MediaQueryList? MediaQueryList { get; set; } 44 | 45 | /// 46 | /// Set by the DOM, the InternalMedia represents the actual Media Query value held by the DOM. 47 | /// 48 | public MediaQueryArgs InternalMedia { get; private set; } = new MediaQueryArgs(); 49 | 50 | protected override void OnInitialized() 51 | { 52 | if (MediaQueryList == null) 53 | { 54 | throw new NullReferenceException("MediaQueryList is null. The MediaQueryList component should be added to the root or MainLayout of your application. Or, set an interactive mode, BlazorSize requires an interactive rendermode."); 55 | } 56 | else 57 | { 58 | MediaQueryList.AddQuery(this); 59 | } 60 | } 61 | 62 | protected override async Task OnAfterRenderAsync(bool firstRender) 63 | { 64 | if (MediaQueryList == null) return; 65 | if (firstRender) 66 | { 67 | await MediaQueryList.Initialize(this); 68 | } 69 | } 70 | 71 | /// 72 | /// Used by the MediaQueryList. Invokes the full media query changed event. 73 | /// 74 | /// MediaQueryArgs 75 | public void MediaQueryChanged(MediaQueryArgs args) 76 | { 77 | MatchesChanged.InvokeAsync(args.Matches); 78 | InternalMedia = args; 79 | if (Matched != null || Unmatched != null) 80 | { 81 | StateHasChanged(); 82 | } 83 | } 84 | 85 | public async ValueTask DisposeAsync() 86 | { 87 | if (MediaQueryList == null) return; 88 | 89 | try 90 | { 91 | await MediaQueryList.RemoveQuery(this); 92 | } 93 | catch (JSDisconnectedException) 94 | { 95 | // Ignore 96 | //https://github.com/EdCharbeneau/BlazorSize/issues/93 97 | } 98 | 99 | } 100 | } 101 | } -------------------------------------------------------------------------------- /BlazorSize/MediaQuery/MediaQueryArgs.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorPro.BlazorSize 2 | { 3 | public class MediaQueryArgs 4 | { 5 | public string? Media { get; set; } 6 | public bool Matches { get; set; } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /BlazorSize/MediaQuery/MediaQueryCache.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | namespace BlazorPro.BlazorSize 3 | { 4 | public class MediaQueryCache 5 | { 6 | /// 7 | /// Initally requested (unmodified) media query. 8 | /// 9 | public string? MediaRequested { get; set; } 10 | 11 | /// 12 | /// Is this item already awating a JavaScript interop call. 13 | /// 14 | public bool Loading { get; set; } 15 | 16 | /// 17 | /// The actual value represented by the DOM. This may differ from the initially requested media query. 18 | /// 19 | public MediaQueryArgs? Value { get; set; } 20 | 21 | /// 22 | /// Media Queries that share a RequestedMedia value. Used to aggregate event handlers and minimize JS calls. 23 | /// 24 | // Nullable because Blazor's lifecycle sometimes GC's this object before the cleanup routines are finished. 25 | public List? MediaQueries { get; set; } = new List(); 26 | } 27 | } 28 | 29 | -------------------------------------------------------------------------------- /BlazorSize/MediaQuery/MediaQueryList.razor: -------------------------------------------------------------------------------- 1 | @namespace BlazorPro.BlazorSize 2 | 3 | @ChildContent 4 | -------------------------------------------------------------------------------- /BlazorSize/MediaQuery/MediaQueryList.razor.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Components; 2 | using Microsoft.JSInterop; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace BlazorPro.BlazorSize 9 | { 10 | public partial class MediaQueryList 11 | { 12 | // IMediaQueryService 13 | [Inject] public IMediaQueryService MqService { get; set; } = null!; 14 | 15 | /// 16 | /// Application content where Media Query components may exist. 17 | /// 18 | [Parameter] public RenderFragment? ChildContent { get; set; } 19 | 20 | protected override async Task OnAfterRenderAsync(bool firstRender) 21 | { 22 | if (firstRender) 23 | { 24 | await MqService.CreateMediaQueryList(DotNetObjectReference.Create(this)); 25 | await base.OnAfterRenderAsync(firstRender); 26 | } 27 | 28 | } 29 | 30 | public void AddQuery(MediaQuery newMediaQuery) => MqService.AddQuery(newMediaQuery); 31 | public async Task RemoveQuery(MediaQuery mediaQuery) => await MqService.RemoveQuery(mediaQuery); 32 | public async Task Initialize(MediaQuery mediaQuery) => await MqService.Initialize(mediaQuery); 33 | /// 34 | /// Called by JavaScript when a media query changes in the dom. 35 | /// 36 | /// 37 | [JSInvokable(nameof(MediaQueryList.MediaQueryChanged))] 38 | public void MediaQueryChanged(MediaQueryArgs args) 39 | { 40 | // cache must be compared by actual value, not RequestedMedia when invoked from JavaScript 41 | // DOM Media value my be different that the initally requested media query value. 42 | var cache = MqService.MediaQueries.Find(q => q.Value?.Media == args.Media); 43 | 44 | if (cache is null) return; 45 | 46 | // Dispatch events to all subscribers 47 | if (cache.MediaQueries is null) return; 48 | foreach (var item in cache.MediaQueries) 49 | { 50 | item.MediaQueryChanged(args); 51 | } 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /BlazorSize/MediaQuery/MediaQueryService.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.JSInterop; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | namespace BlazorPro.BlazorSize 8 | { 9 | public class MediaQueryService : IAsyncDisposable, IMediaQueryService 10 | { 11 | private readonly Lazy> moduleTask; 12 | 13 | public MediaQueryService(IJSRuntime jsRuntime) 14 | { 15 | moduleTask = new(() => jsRuntime.InvokeAsync( 16 | "import", "./_content/BlazorPro.BlazorSize/blazorSizeMediaModule.js").AsTask()); 17 | } 18 | 19 | // JavaScript uses this value for tracking MediaQueryList instances. 20 | private DotNetObjectReference DotNetInstance { get; set; } = null!; 21 | private readonly List mediaQueries = new(); 22 | public List MediaQueries => mediaQueries; 23 | 24 | private MediaQueryCache? GetMediaQueryFromCache(string byMedia) => mediaQueries?.Find(q => q.MediaRequested == byMedia); 25 | 26 | public void AddQuery(MediaQuery newMediaQuery) 27 | { 28 | MediaQueryCache? cache = GetMediaQueryFromCache(byMedia: newMediaQuery.Media); 29 | if (cache is null) 30 | { 31 | cache = new MediaQueryCache 32 | { 33 | MediaRequested = newMediaQuery.Media, 34 | }; 35 | mediaQueries.Add(cache); 36 | } 37 | cache.MediaQueries?.Add(newMediaQuery); 38 | } 39 | 40 | public async Task RemoveQuery(MediaQuery mediaQuery) 41 | { 42 | if (mediaQuery is null) return; 43 | 44 | MediaQueryCache? cache = GetMediaQueryFromCache(byMedia: mediaQuery.Media); 45 | 46 | if (cache is null || cache.MediaQueries is null) return; 47 | 48 | try 49 | { 50 | cache.MediaQueries.Remove(mediaQuery); 51 | if (cache.MediaQueries.Any()) return; 52 | mediaQueries.Remove(cache); 53 | IJSObjectReference module = await moduleTask.Value; 54 | await module.InvokeVoidAsync($"removeMediaQuery", DotNetInstance, mediaQuery.InternalMedia.Media); 55 | } 56 | catch (Exception) 57 | { 58 | //https://github.com/EdCharbeneau/BlazorSize/issues/93 59 | //https://stackoverflow.com/questions/72488563/blazor-server-side-application-throwing-system-invalidoperationexception-javas 60 | } 61 | } 62 | 63 | public async Task Initialize(MediaQuery mediaQuery) 64 | { 65 | if (mediaQuery?.Media is null) return; 66 | var cache = GetMediaQueryFromCache(byMedia: mediaQuery.Media); 67 | if (cache is null) return; 68 | 69 | if (cache.Value is null) 70 | { 71 | // If we don't flag the cache as loading, duplicate requests will be sent async. 72 | // Duplicate requests = poor performance, esp with web sockets. 73 | if (!cache.Loading) 74 | { 75 | cache.Loading = true; 76 | 77 | var module = await moduleTask.Value; 78 | var task = module.InvokeAsync($"addMediaQueryToList", DotNetInstance, cache.MediaRequested); 79 | cache.Value = await task; 80 | cache.Loading = task.IsCompleted; 81 | // When loading is complete dispatch an update to all subscribers. 82 | cache.MediaQueries ??= new List(); 83 | foreach (var item in cache.MediaQueries) 84 | { 85 | item.MediaQueryChanged(cache.Value); 86 | } 87 | } 88 | } 89 | else 90 | { 91 | var module = await moduleTask.Value; 92 | var task = module.InvokeAsync($"getMediaQueryArgs", cache.MediaRequested); 93 | cache.Value = await task; 94 | 95 | mediaQuery.MediaQueryChanged(cache.Value); 96 | } 97 | } 98 | 99 | public async Task CreateMediaQueryList(DotNetObjectReference dotNetObjectReference) 100 | { 101 | DotNetInstance = dotNetObjectReference; 102 | var module = await moduleTask.Value; 103 | await module.InvokeVoidAsync("addMediaQueryList", dotNetObjectReference); 104 | } 105 | 106 | public async ValueTask DisposeAsync() 107 | { 108 | try 109 | { 110 | if (DotNetInstance is null) return; 111 | 112 | var module = await moduleTask.Value; 113 | await module.InvokeVoidAsync("removeMediaQueryList", DotNetInstance); 114 | DotNetInstance.Dispose(); 115 | await module.DisposeAsync(); 116 | GC.SuppressFinalize(this); 117 | } 118 | catch (Exception) 119 | { 120 | //https://github.com/EdCharbeneau/BlazorSize/issues/93 121 | //https://stackoverflow.com/questions/72488563/blazor-server-side-application-throwing-system-invalidoperationexception-javas 122 | } 123 | } 124 | 125 | } 126 | 127 | } -------------------------------------------------------------------------------- /BlazorSize/Resize/BrowserWindowSize.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace BlazorPro.BlazorSize 4 | { 5 | public class BrowserWindowSize : EventArgs 6 | { 7 | public int Height { get; set; } 8 | public int Width { get; set; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /BlazorSize/Resize/IResizeListener.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | 4 | namespace BlazorPro.BlazorSize 5 | { 6 | public interface IResizeListener 7 | { 8 | event EventHandler OnResized; 9 | 10 | ValueTask GetBrowserWindowSize(); 11 | ValueTask MatchMedia(string mediaQuery); 12 | } 13 | } -------------------------------------------------------------------------------- /BlazorSize/Resize/ResizeListener.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Options; 2 | using Microsoft.JSInterop; 3 | using System; 4 | using System.Threading.Tasks; 5 | 6 | namespace BlazorPro.BlazorSize 7 | { 8 | public class ResizeListener : IResizeListener, IAsyncDisposable 9 | { 10 | private readonly Lazy> moduleTask; 11 | 12 | private readonly ResizeOptions options; 13 | 14 | private bool disposed; 15 | public ResizeListener(IJSRuntime jsRuntime, IOptions? options = null) 16 | { 17 | this.options = options?.Value ?? new ResizeOptions(); 18 | moduleTask = new(() => jsRuntime.InvokeAsync( 19 | "import", "./_content/BlazorPro.BlazorSize/blazorSizeResizeModule.js").AsTask()); 20 | } 21 | 22 | private EventHandler? onResized; 23 | 24 | /// 25 | /// Subscribe to the browsers resize() event. 26 | /// 27 | public event EventHandler? OnResized 28 | { 29 | add => Subscribe(value); 30 | remove => Unsubscribe(value); 31 | } 32 | 33 | private void Unsubscribe(EventHandler? value) 34 | { 35 | onResized -= value; 36 | if (onResized is null) 37 | { 38 | Cancel().ConfigureAwait(false); 39 | } 40 | } 41 | 42 | private void Subscribe(EventHandler? value) 43 | { 44 | if (onResized is null) 45 | { 46 | Task.Run(async () => await Start()); 47 | } 48 | onResized += value; 49 | } 50 | 51 | private async ValueTask Start() 52 | { 53 | var module = await moduleTask.Value; 54 | await module.InvokeVoidAsync("listenForResize", DotNetObjectReference.Create(this), options); 55 | } 56 | 57 | private async ValueTask Cancel() 58 | { 59 | var module = await moduleTask.Value; 60 | await module.InvokeVoidAsync("cancelListener"); 61 | } 62 | 63 | /// 64 | /// Determine if the Document matches the provided media query. 65 | /// 66 | /// 67 | /// Returns true if matched. 68 | public async ValueTask MatchMedia(string mediaQuery) 69 | { 70 | var module = await moduleTask.Value; 71 | return await module.InvokeAsync("matchMedia", mediaQuery); 72 | } 73 | 74 | /// 75 | /// Get the current BrowserWindowSize, this includes the Height and Width of the document. 76 | /// 77 | /// 78 | public async ValueTask GetBrowserWindowSize() 79 | { 80 | var module = await moduleTask.Value; 81 | return await module.InvokeAsync("getBrowserWindowSize"); 82 | } 83 | 84 | /// 85 | /// Invoked by jsInterop, use the OnResized event handler to subscribe. 86 | /// 87 | /// 88 | [JSInvokable] 89 | public void RaiseOnResized(BrowserWindowSize browserWindowSize) => 90 | onResized?.Invoke(this, browserWindowSize); 91 | 92 | protected virtual void Dispose(bool disposing) 93 | { 94 | if (!disposed) 95 | { 96 | if (disposing) 97 | { 98 | onResized = null; 99 | } 100 | disposed = true; 101 | } 102 | } 103 | 104 | public async ValueTask DisposeAsync() 105 | { 106 | try 107 | { 108 | 109 | 110 | if (moduleTask.IsValueCreated) 111 | { 112 | var module = await moduleTask.Value; 113 | await module.DisposeAsync(); 114 | } 115 | Dispose(true); 116 | GC.SuppressFinalize(this); 117 | } 118 | catch (Exception) 119 | { 120 | //https://stackoverflow.com/questions/72488563/blazor-server-side-application-throwing-system-invalidoperationexception-javas 121 | } 122 | } 123 | } 124 | } -------------------------------------------------------------------------------- /BlazorSize/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using Microsoft.AspNetCore.Components.Web 2 | -------------------------------------------------------------------------------- /BlazorSize/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'ts-jest', 3 | testPathIgnorePatterns: [".d.ts", ".js"], 4 | testEnvironment: 'jsdom', 5 | }; -------------------------------------------------------------------------------- /BlazorSize/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "blazorsize", 3 | "version": "1.0.0", 4 | "description": "", 5 | "private": true, 6 | "scripts": { 7 | "test": "tsc -p tsconfig.json && jest && npm run posttest", 8 | "posttest": "rimraf scripts/tests/*.js", 9 | "prebuild": "rimraf wwwroot", 10 | "build": "npm run prebuild && tsc -p tsconfig.build.json", 11 | "record": "npx playwright codegen --viewport-size=600,800" 12 | }, 13 | "author": "", 14 | "license": "ISC", 15 | "devDependencies": { 16 | "@types/jest": "^29.2.5", 17 | "jest": "^29.3.1", 18 | "jest-environment-jsdom": "^29.3.1", 19 | "jest-matchmedia-mock": "^1.1.0", 20 | "node-notifier": "^10.0.1", 21 | "rimraf": "^3.0.2", 22 | "ts-jest": "^29.0.3", 23 | "typescript": "^4.9.4" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /BlazorSize/readme.md: -------------------------------------------------------------------------------- 1 | BlazorSize is a JavaScript interop library for Blazor that is used to detect the Browser's current size, change in size, and test media queries. 2 | 3 | BlazorSize was designed to allow Razor Components to implement adaptive rendering. Handle mobile/desktop rendering changes at runtime with ease and complement your apps CSS media queries with programmatic work flows. 4 | 5 | [For documentation please refer to the Wiki section of this GitHub repo.](https://github.com/EdCharbeneau/BlazorSize/wiki) 6 | 7 |
8 | 9 | A component with similar functionality of BlazorSize can also be found in [Telerik UI for Blazor](https://demos.telerik.com/blazor-ui/mediaquery/overview?utm_source=EdCharbeneau&utm_medium=cpm&utm_campaign=blazor-github-sponsored-message). 10 | 11 | [![Telerik UI for Blazor, Increase productivity and cut costs in half. Try Now](https://github.com/EdCharbeneau/BlazorSize/blob/master/_sponsors/Telerik/Blazor-300x250.png?raw=true)]((https://www.telerik.com/campaigns/blazor/free-trial-1?utm_source=EdCharbeneau&utm_medium=cpm&utm_campaign=blazor-github-sponsored-message)) 12 | 13 | **If you’re already a Telerik customer there’s no need to use BlazorSize**, instead you may use the TelerikMediaQuery component. If you’re looking for a full component library of 100+ components, world class support, and rich documentation then [give Telerik UI for Blazor a try for free](https://www.telerik.com/campaigns/blazor/free-trial-1?utm_source=EdCharbeneau&utm_medium=cpm&utm_campaign=blazor-github-sponsored-message). 14 | -------------------------------------------------------------------------------- /BlazorSize/scripts/blazorSizeMedia.ts: -------------------------------------------------------------------------------- 1 | class MediaQueryListItem { 2 | constructor(id: number | string) { 3 | this.id = id; 4 | this.dotnetCallback = (args) => { }; 5 | this.mediaQueries = []; 6 | } 7 | public id: number | string; 8 | public dotnetCallback: (ev: MediaQueryListEvent) => void; 9 | public mediaQueries: MediaQueryList[]; 10 | } 11 | 12 | interface MediaQueryArgs { 13 | media: string; 14 | matches: boolean; 15 | } 16 | 17 | export class BlazorSizeMedia { 18 | mediaQueryLists: MediaQueryListItem[] = new Array(); 19 | 20 | //private toMediaQueryArgs = (mql: MediaQueryArgs) => ({ media: mql.media, matches: mql.matches }); 21 | private getMediaQueryListById = (id: number | string) => { 22 | let mediaQueryList = this.mediaQueryLists.find(mql => mql.id === id) 23 | if (mediaQueryList === undefined) { 24 | throw new Error("dotnet reference was not found in the collection of media query lists"); 25 | } 26 | return mediaQueryList; 27 | }; 28 | 29 | getMediaQueryArgs(mediaQuery: string) { 30 | let mq = window.matchMedia(mediaQuery); 31 | return { matches: mq.matches, media: mq.media } as MediaQueryArgs; 32 | } 33 | 34 | addMediaQueryToList(dotnetMql: any, mediaQuery: string) { 35 | let mq = window.matchMedia(mediaQuery); 36 | let mediaQueryList = this.getMediaQueryListById(dotnetMql._id); 37 | //console.log(`[BlazorSize] MediaQuery Read - media: ${mq.media} matches: ${mq.matches}`); 38 | mq.addEventListener('change', mediaQueryList.dotnetCallback); 39 | mediaQueryList.mediaQueries.push(mq); 40 | return { matches: mq.matches, media: mq.media } as MediaQueryArgs; 41 | } 42 | 43 | callbackReference(dotnet: any) { 44 | return (ev: MediaQueryListEvent) => { 45 | //console.log(`[BlazorSize] MediaQuery Changed - media: ${ev.media} matches: ${ev.matches}`); 46 | dotnet.invokeMethodAsync("MediaQueryChanged", { matches: ev.matches, media: ev.media } as MediaQueryArgs); 47 | } 48 | } 49 | 50 | addMediaQueryList(dotnet: any) { 51 | let list = new MediaQueryListItem(dotnet._id); 52 | list.dotnetCallback = this.callbackReference(dotnet); 53 | this.mediaQueryLists.push(list); 54 | } 55 | 56 | removeMediaQuery(dotnetMql: any, mediaQuery: string) { 57 | let list = this.getMediaQueryListById(dotnetMql._id); 58 | let queries = list.mediaQueries; 59 | let toRemove = queries.find(q => q.media == mediaQuery); 60 | toRemove?.removeEventListener('change',list.dotnetCallback); 61 | list.mediaQueries = queries.filter(q => q.media !== toRemove?.media); 62 | } 63 | 64 | removeMediaQueryList(dotnetMql: any) { 65 | // Get the media query from the list 66 | let list = this.getMediaQueryListById(dotnetMql._id); 67 | // Remove all event handlers 68 | list.mediaQueries.forEach(q => q.removeEventListener('change', list.dotnetCallback)); 69 | // Remove the item from the list 70 | this.mediaQueryLists = this.mediaQueryLists.filter(f => f.id !== dotnetMql._id); 71 | } 72 | } -------------------------------------------------------------------------------- /BlazorSize/scripts/blazorSizeMediaModule.ts: -------------------------------------------------------------------------------- 1 | import { BlazorSizeMedia } from './blazorSizeMedia.js'; 2 | 3 | let instance = new BlazorSizeMedia(); 4 | 5 | // Uncomment to enable console inspection of the instance 6 | // (window as any).bs_instance = instance; 7 | 8 | // When MQL is created, it tracks a NET Ref 9 | export function addMediaQueryList(dotnet: any) { 10 | instance.addMediaQueryList(dotnet); 11 | } 12 | 13 | // OnDispose this method cleans the MQL instance 14 | export function removeMediaQueryList(dotnetMql: any) { 15 | instance.removeMediaQueryList(dotnetMql); 16 | } 17 | 18 | // When MediaQuery instance is created track media query in JavaScript 19 | export function addMediaQueryToList(dotnetMql: any, mediaQuery: string) { 20 | return instance.addMediaQueryToList(dotnetMql, mediaQuery); 21 | } 22 | 23 | // When MediaQuery instance is disposed remove media query in JavaScript 24 | export function removeMediaQuery(dotnetMql: any, mediaQuery: string) { 25 | instance.removeMediaQuery(dotnetMql, mediaQuery); 26 | } 27 | 28 | // Get media query value for cache after app loads 29 | export function getMediaQueryArgs(mediaQuery: string) { 30 | return instance.getMediaQueryArgs(mediaQuery); 31 | } 32 | 33 | -------------------------------------------------------------------------------- /BlazorSize/scripts/blazorSizeResize.ts: -------------------------------------------------------------------------------- 1 | export class ResizeOptions { 2 | reportRate: number = 300; 3 | enableLogging: boolean = false; 4 | suppressInitEvent: boolean = false; 5 | } 6 | 7 | export class ResizeListener { 8 | 9 | private logger: (message: any) => void = (message: any) => { }; 10 | private options: ResizeOptions = new ResizeOptions(); 11 | private throttleResizeHandlerId: number = -1; 12 | private dotnet: any; 13 | 14 | private resizeHandler = () => { 15 | this.dotnet.invokeMethodAsync( 16 | 'RaiseOnResized', { 17 | height: window.innerHeight, 18 | width: window.innerWidth 19 | }); 20 | this.logger("[BlazorSize] RaiseOnResized invoked"); 21 | } 22 | 23 | public listenForResize(dotnetRef: any, options: ResizeOptions) { 24 | this.options = options; 25 | this.dotnet = dotnetRef; 26 | this.logger = options.enableLogging ? console.log : (message: any) => { }; 27 | this.logger(`[BlazorSize] Reporting resize events at rate of: ${this.options.reportRate}ms`); 28 | window.addEventListener("resize", this.throttleResizeHandler); 29 | if (!this.options.suppressInitEvent) { 30 | this.resizeHandler(); 31 | } 32 | } 33 | 34 | private throttleResizeHandler = () => { 35 | clearTimeout(this.throttleResizeHandlerId); 36 | this.throttleResizeHandlerId = window.setTimeout(this.resizeHandler, this.options.reportRate); 37 | } 38 | 39 | 40 | 41 | public cancelListener() { 42 | window.removeEventListener("resize", this.throttleResizeHandler); 43 | } 44 | 45 | public matchMedia(query: string) { 46 | let m = window.matchMedia(query).matches; 47 | this.logger(`[BlazorSize] matchMedia "${query}": ${m}`); 48 | return m; 49 | } 50 | 51 | public getBrowserWindowSize() { 52 | return { 53 | height: window.innerHeight, 54 | width: window.innerWidth 55 | }; 56 | } 57 | } -------------------------------------------------------------------------------- /BlazorSize/scripts/blazorSizeResizeModule.ts: -------------------------------------------------------------------------------- 1 | import { ResizeListener, ResizeOptions } from './blazorSizeResize.js'; 2 | 3 | let instance = new ResizeListener(); 4 | 5 | export function listenForResize(dotnetRef: any, options: ResizeOptions) { 6 | instance.listenForResize(dotnetRef, options); 7 | } 8 | 9 | export function cancelListener() { 10 | instance.cancelListener(); 11 | } 12 | 13 | export function matchMedia(query: string) { 14 | return instance.matchMedia(query); 15 | } 16 | 17 | export function getBrowserWindowSize() { 18 | return instance.getBrowserWindowSize(); 19 | } -------------------------------------------------------------------------------- /BlazorSize/scripts/tests/blazorSize.test.ts: -------------------------------------------------------------------------------- 1 | import { BlazorSizeMedia } from '../blazorSizeMedia'; 2 | import MatchMediaMock from 'jest-matchmedia-mock'; 3 | import MatchMedia from 'jest-matchmedia-mock'; 4 | 5 | let matchMedia: MatchMedia; 6 | 7 | describe('Your testing module', () => { 8 | beforeAll(() => { 9 | matchMedia = new MatchMediaMock(); 10 | const mediaQuery = '(min-width: 240px) and (max-width: 767px)'; 11 | matchMedia.useMediaQuery(mediaQuery); 12 | }); 13 | 14 | afterEach(() => { 15 | matchMedia.clear(); 16 | }); 17 | 18 | let fakeDotNetList = { 19 | _id: 1, 20 | invokeMethodAsync(method: string, callback: any) { } 21 | } 22 | 23 | let fakeDotNetQuery = { 24 | _id: 9, 25 | } 26 | 27 | 28 | test('Can crate', () => { 29 | let x = new BlazorSizeMedia(); 30 | expect(x.mediaQueryLists).toHaveLength(0); 31 | }) 32 | 33 | test('Can add MediaQueryList', () => { 34 | let x = new BlazorSizeMedia(); 35 | x.addMediaQueryList(fakeDotNetList); 36 | expect(x.mediaQueryLists).toHaveLength(1); 37 | }) 38 | 39 | test('Can add MediaQuery', () => { 40 | let x = new BlazorSizeMedia(); 41 | let query = '(min-width: 240px) and (max-width: 767px)'; 42 | x.addMediaQueryList(fakeDotNetList); 43 | let result = x.addMediaQueryToList(fakeDotNetList, query); 44 | expect(x.mediaQueryLists[0].mediaQueries.length).toBe(1); 45 | expect(result.media).toBe(query); 46 | expect(result.matches).toBe(true); 47 | }) 48 | 49 | test('Can remove MediaQuery', () => { 50 | let x = new BlazorSizeMedia(); 51 | let query = '(min-width: 240px) and (max-width: 767px)'; 52 | x.addMediaQueryList(fakeDotNetList); 53 | let result = x.addMediaQueryToList(fakeDotNetList, query); 54 | x.removeMediaQuery(fakeDotNetList, query); 55 | expect(matchMedia.getListeners(query).length).toBe(0); 56 | expect(x.mediaQueryLists[0].mediaQueries.length).toBe(0); 57 | }) 58 | 59 | test('Can remove MediaQueryList', () => { 60 | let x = new BlazorSizeMedia(); 61 | let query = '(min-width: 240px) and (max-width: 767px)'; 62 | x.addMediaQueryList(fakeDotNetList); 63 | let result = x.addMediaQueryToList(fakeDotNetList, query); 64 | x.removeMediaQueryList(fakeDotNetList); 65 | expect(matchMedia.getListeners(query).length).toBe(0); 66 | expect(x.mediaQueryLists.length).toBe(0); 67 | }) 68 | 69 | }) -------------------------------------------------------------------------------- /BlazorSize/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./wwwroot", 4 | //"noImplicitAny": false, 5 | //"noEmitOnError": true, 6 | //"removeComments": true, 7 | //"sourceMap": false, 8 | "target": "ES6", 9 | "moduleResolution": "node", 10 | "lib": [ "ES2016", "DOM"] 11 | }, 12 | "exclude": [ 13 | "node_modules", 14 | "wwwroot", 15 | "**/*.test.ts" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /BlazorSize/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./scripts/tests", 4 | "esModuleInterop": true, 5 | "skipLibCheck": true, 6 | /* Basic Options */ 7 | // "incremental": true, /* Enable incremental compilation */ 8 | "target": "es6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */ 9 | "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ 10 | "lib": [ "ES2015", "DOM" ], /* Specify library files to be included in the compilation. */ 11 | // "allowJs": true, /* Allow javascript files to be compiled. */ 12 | // "checkJs": true, /* Report errors in .js files. */ 13 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ 14 | // "declaration": true, /* Generates corresponding '.d.ts' file. */ 15 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ 16 | // "sourceMap": true, /* Generates corresponding '.map' file. */ 17 | //"outFile": "./blazorSize.js", /* Concatenate and emit output to single file. */ 18 | //"outDir": "./wwwroot", /* Redirect output structure to the directory. */ 19 | // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ 20 | // "composite": true, /* Enable project compilation */ 21 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ 22 | // "removeComments": true, /* Do not emit comments to output. */ 23 | // "noEmit": true, /* Do not emit outputs. */ 24 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */ 25 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ 26 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ 27 | 28 | /* Strict Type-Checking Options */ 29 | "strict": true, /* Enable all strict type-checking options. */ 30 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ 31 | // "strictNullChecks": true, /* Enable strict null checks. */ 32 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */ 33 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ 34 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ 35 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ 36 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ 37 | 38 | /* Additional Checks */ 39 | // "noUnusedLocals": true, /* Report errors on unused locals. */ 40 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 41 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 42 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 43 | 44 | /* Module Resolution Options */ 45 | // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 46 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ 47 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ 48 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ 49 | // "typeRoots": [], /* List of folders to include type definitions from. */ 50 | // "types": [], /* Type declaration files to be included in compilation. */ 51 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ 52 | "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ 53 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ 54 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 55 | 56 | /* Source Map Options */ 57 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ 58 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 59 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ 60 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ 61 | 62 | /* Experimental Options */ 63 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ 64 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ 65 | 66 | /* Advanced Options */ 67 | "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /BlazorWebApp/BlazorWebApp.Client/BlazorWebApp.Client.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net9.0 5 | enable 6 | enable 7 | true 8 | Default 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /BlazorWebApp/BlazorWebApp.Client/Layout/MainLayout.razor: -------------------------------------------------------------------------------- 1 | @inherits LayoutComponentBase 2 | 3 |
4 | 7 | 8 |
9 |
10 | About 11 |
12 | 13 |
14 | @Body 15 |
16 |
17 |
18 | 19 |
20 | An unhandled error has occurred. 21 | Reload 22 | 🗙 23 |
24 | -------------------------------------------------------------------------------- /BlazorWebApp/BlazorWebApp.Client/Layout/MainLayout.razor.css: -------------------------------------------------------------------------------- 1 | .page { 2 | position: relative; 3 | display: flex; 4 | flex-direction: column; 5 | } 6 | 7 | main { 8 | flex: 1; 9 | } 10 | 11 | .sidebar { 12 | background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%); 13 | } 14 | 15 | .top-row { 16 | background-color: #f7f7f7; 17 | border-bottom: 1px solid #d6d5d5; 18 | justify-content: flex-end; 19 | height: 3.5rem; 20 | display: flex; 21 | align-items: center; 22 | } 23 | 24 | .top-row ::deep a, .top-row ::deep .btn-link { 25 | white-space: nowrap; 26 | margin-left: 1.5rem; 27 | text-decoration: none; 28 | } 29 | 30 | .top-row ::deep a:hover, .top-row ::deep .btn-link:hover { 31 | text-decoration: underline; 32 | } 33 | 34 | .top-row ::deep a:first-child { 35 | overflow: hidden; 36 | text-overflow: ellipsis; 37 | } 38 | 39 | @media (max-width: 640.98px) { 40 | .top-row { 41 | justify-content: space-between; 42 | } 43 | 44 | .top-row ::deep a, .top-row ::deep .btn-link { 45 | margin-left: 0; 46 | } 47 | } 48 | 49 | @media (min-width: 641px) { 50 | .page { 51 | flex-direction: row; 52 | } 53 | 54 | .sidebar { 55 | width: 250px; 56 | height: 100vh; 57 | position: sticky; 58 | top: 0; 59 | } 60 | 61 | .top-row { 62 | position: sticky; 63 | top: 0; 64 | z-index: 1; 65 | } 66 | 67 | .top-row.auth ::deep a:first-child { 68 | flex: 1; 69 | text-align: right; 70 | width: 0; 71 | } 72 | 73 | .top-row, article { 74 | padding-left: 2rem !important; 75 | padding-right: 1.5rem !important; 76 | } 77 | } 78 | 79 | #blazor-error-ui { 80 | background: lightyellow; 81 | bottom: 0; 82 | box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2); 83 | display: none; 84 | left: 0; 85 | padding: 0.6rem 1.25rem 0.7rem 1.25rem; 86 | position: fixed; 87 | width: 100%; 88 | z-index: 1000; 89 | } 90 | 91 | #blazor-error-ui .dismiss { 92 | cursor: pointer; 93 | position: absolute; 94 | right: 0.75rem; 95 | top: 0.5rem; 96 | } 97 | -------------------------------------------------------------------------------- /BlazorWebApp/BlazorWebApp.Client/Layout/NavMenu.razor: -------------------------------------------------------------------------------- 1 |  6 | 7 | 8 | 9 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /BlazorWebApp/BlazorWebApp.Client/Layout/NavMenu.razor.css: -------------------------------------------------------------------------------- 1 | .navbar-toggler { 2 | appearance: none; 3 | cursor: pointer; 4 | width: 3.5rem; 5 | height: 2.5rem; 6 | color: white; 7 | position: absolute; 8 | top: 0.5rem; 9 | right: 1rem; 10 | border: 1px solid rgba(255, 255, 255, 0.1); 11 | background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e") no-repeat center/1.75rem rgba(255, 255, 255, 0.1); 12 | } 13 | 14 | .navbar-toggler:checked { 15 | background-color: rgba(255, 255, 255, 0.5); 16 | } 17 | 18 | .top-row { 19 | height: 3.5rem; 20 | background-color: rgba(0,0,0,0.4); 21 | } 22 | 23 | .navbar-brand { 24 | font-size: 1.1rem; 25 | } 26 | 27 | .bi { 28 | display: inline-block; 29 | position: relative; 30 | width: 1.25rem; 31 | height: 1.25rem; 32 | margin-right: 0.75rem; 33 | top: -1px; 34 | background-size: cover; 35 | } 36 | 37 | .bi-house-door-fill-nav-menu { 38 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-house-door-fill' viewBox='0 0 16 16'%3E%3Cpath d='M6.5 14.5v-3.505c0-.245.25-.495.5-.495h2c.25 0 .5.25.5.5v3.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5Z'/%3E%3C/svg%3E"); 39 | } 40 | 41 | .bi-plus-square-fill-nav-menu { 42 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-plus-square-fill' viewBox='0 0 16 16'%3E%3Cpath d='M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm6.5 4.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3a.5.5 0 0 1 1 0z'/%3E%3C/svg%3E"); 43 | } 44 | 45 | .bi-list-nested-nav-menu { 46 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-list-nested' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M4.5 11.5A.5.5 0 0 1 5 11h10a.5.5 0 0 1 0 1H5a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 3 7h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 1 3h10a.5.5 0 0 1 0 1H1a.5.5 0 0 1-.5-.5z'/%3E%3C/svg%3E"); 47 | } 48 | 49 | .nav-item { 50 | font-size: 0.9rem; 51 | padding-bottom: 0.5rem; 52 | } 53 | 54 | .nav-item:first-of-type { 55 | padding-top: 1rem; 56 | } 57 | 58 | .nav-item:last-of-type { 59 | padding-bottom: 1rem; 60 | } 61 | 62 | .nav-item ::deep .nav-link { 63 | color: #d7d7d7; 64 | background: none; 65 | border: none; 66 | border-radius: 4px; 67 | height: 3rem; 68 | display: flex; 69 | align-items: center; 70 | line-height: 3rem; 71 | width: 100%; 72 | } 73 | 74 | .nav-item ::deep a.active { 75 | background-color: rgba(255,255,255,0.37); 76 | color: white; 77 | } 78 | 79 | .nav-item ::deep .nav-link:hover { 80 | background-color: rgba(255,255,255,0.1); 81 | color: white; 82 | } 83 | 84 | .nav-scrollable { 85 | display: none; 86 | } 87 | 88 | .navbar-toggler:checked ~ .nav-scrollable { 89 | display: block; 90 | } 91 | 92 | @media (min-width: 641px) { 93 | .navbar-toggler { 94 | display: none; 95 | } 96 | 97 | .nav-scrollable { 98 | /* Never collapse the sidebar for wide screens */ 99 | display: block; 100 | 101 | /* Allow sidebar to scroll for tall menus */ 102 | height: calc(100vh - 3.5rem); 103 | overflow-y: auto; 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /BlazorWebApp/BlazorWebApp.Client/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Components.WebAssembly.Hosting; 2 | using BlazorPro.BlazorSize; 3 | using TestComponents; 4 | 5 | var builder = WebAssemblyHostBuilder.CreateDefault(args); 6 | builder.Services.AddResizeListener(); 7 | builder.Services.AddMediaQueryService(); 8 | builder.Services.AddScoped(); 9 | await builder.Build().RunAsync(); 10 | -------------------------------------------------------------------------------- /BlazorWebApp/BlazorWebApp.Client/Routes.razor: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /BlazorWebApp/BlazorWebApp.Client/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using System.Net.Http 2 | @using System.Net.Http.Json 3 | @using Microsoft.AspNetCore.Components.Forms 4 | @using Microsoft.AspNetCore.Components.Routing 5 | @using Microsoft.AspNetCore.Components.Web 6 | @using static Microsoft.AspNetCore.Components.Web.RenderMode 7 | @using Microsoft.AspNetCore.Components.Web.Virtualization 8 | @using Microsoft.JSInterop 9 | @using BlazorWebApp.Client 10 | @using BlazorPro.BlazorSize -------------------------------------------------------------------------------- /BlazorWebApp/BlazorWebApp.Client/wwwroot/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /BlazorWebApp/BlazorWebApp.Client/wwwroot/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /BlazorWebApp/BlazorWebApp/BlazorWebApp.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net9.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /BlazorWebApp/BlazorWebApp/Components/App.razor: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /BlazorWebApp/BlazorWebApp/Components/Pages/Error.razor: -------------------------------------------------------------------------------- 1 | @page "/Error" 2 | @using System.Diagnostics 3 | 4 | Error 5 | 6 |

Error.

7 |

An error occurred while processing your request.

8 | 9 | @if (ShowRequestId) 10 | { 11 |

12 | Request ID: @RequestId 13 |

14 | } 15 | 16 |

Development Mode

17 |

18 | Swapping to Development environment will display more detailed information about the error that occurred. 19 |

20 |

21 | The Development environment shouldn't be enabled for deployed applications. 22 | It can result in displaying sensitive information from exceptions to end users. 23 | For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development 24 | and restarting the app. 25 |

26 | 27 | @code{ 28 | [CascadingParameter] 29 | private HttpContext? HttpContext { get; set; } 30 | 31 | private string? RequestId { get; set; } 32 | private bool ShowRequestId => !string.IsNullOrEmpty(RequestId); 33 | 34 | protected override void OnInitialized() => 35 | RequestId = Activity.Current?.Id ?? HttpContext?.TraceIdentifier; 36 | } 37 | -------------------------------------------------------------------------------- /BlazorWebApp/BlazorWebApp/Components/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using System.Net.Http 2 | @using System.Net.Http.Json 3 | @using Microsoft.AspNetCore.Components.Forms 4 | @using Microsoft.AspNetCore.Components.Routing 5 | @using Microsoft.AspNetCore.Components.Web 6 | @using static Microsoft.AspNetCore.Components.Web.RenderMode 7 | @using Microsoft.AspNetCore.Components.Web.Virtualization 8 | @using Microsoft.JSInterop 9 | @using BlazorWebApp 10 | @using BlazorWebApp.Client 11 | @using BlazorWebApp.Components 12 | -------------------------------------------------------------------------------- /BlazorWebApp/BlazorWebApp/Program.cs: -------------------------------------------------------------------------------- 1 | using BlazorWebApp.Client; 2 | using BlazorWebApp.Components; 3 | using BlazorPro.BlazorSize; 4 | using TestComponents; 5 | 6 | var builder = WebApplication.CreateBuilder(args); 7 | 8 | // Add services to the container. 9 | 10 | builder.Services.AddRazorComponents() 11 | .AddInteractiveServerComponents() 12 | .AddInteractiveWebAssemblyComponents(); 13 | builder.Services.AddResizeListener(); 14 | builder.Services.AddMediaQueryService(); 15 | builder.Services.AddScoped(); 16 | 17 | var app = builder.Build(); 18 | 19 | // Configure the HTTP request pipeline. 20 | if (app.Environment.IsDevelopment()) 21 | { 22 | app.UseWebAssemblyDebugging(); 23 | } 24 | else 25 | { 26 | app.UseExceptionHandler("/Error", createScopeForErrors: true); 27 | // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. 28 | app.UseHsts(); 29 | } 30 | 31 | app.UseHttpsRedirection(); 32 | 33 | app.UseStaticFiles(); 34 | app.UseAntiforgery(); 35 | 36 | app.MapRazorComponents() 37 | .AddInteractiveServerRenderMode() 38 | .AddInteractiveWebAssemblyRenderMode() 39 | .AddAdditionalAssemblies(new[] { typeof(BlazorWebApp.Client._Imports).Assembly, typeof(TestComponents._Imports).Assembly }); 40 | 41 | app.Run(); 42 | -------------------------------------------------------------------------------- /BlazorWebApp/BlazorWebApp/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/launchsettings.json", 3 | "iisSettings": { 4 | "windowsAuthentication": false, 5 | "anonymousAuthentication": true, 6 | "iisExpress": { 7 | "applicationUrl": "http://localhost:30799", 8 | "sslPort": 44377 9 | } 10 | }, 11 | "profiles": { 12 | "http": { 13 | "commandName": "Project", 14 | "dotnetRunMessages": true, 15 | "launchBrowser": true, 16 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 17 | "applicationUrl": "http://localhost:5094", 18 | "environmentVariables": { 19 | "ASPNETCORE_ENVIRONMENT": "Development" 20 | } 21 | }, 22 | "https": { 23 | "commandName": "Project", 24 | "dotnetRunMessages": true, 25 | "launchBrowser": true, 26 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 27 | "applicationUrl": "https://localhost:7249;http://localhost:5094", 28 | "environmentVariables": { 29 | "ASPNETCORE_ENVIRONMENT": "Development" 30 | } 31 | }, 32 | "IIS Express": { 33 | "commandName": "IISExpress", 34 | "launchBrowser": true, 35 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 36 | "environmentVariables": { 37 | "ASPNETCORE_ENVIRONMENT": "Development" 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /BlazorWebApp/BlazorWebApp/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /BlazorWebApp/BlazorWebApp/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "AllowedHosts": "*" 9 | } 10 | -------------------------------------------------------------------------------- /BlazorWebApp/BlazorWebApp/wwwroot/app.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; 3 | } 4 | 5 | a, .btn-link { 6 | color: #006bb7; 7 | } 8 | 9 | .btn-primary { 10 | color: #fff; 11 | background-color: #1b6ec2; 12 | border-color: #1861ac; 13 | } 14 | 15 | .btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus { 16 | box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb; 17 | } 18 | 19 | .content { 20 | padding-top: 1.1rem; 21 | } 22 | 23 | h1:focus { 24 | outline: none; 25 | } 26 | 27 | .valid.modified:not([type=checkbox]) { 28 | outline: 1px solid #26b050; 29 | } 30 | 31 | .invalid { 32 | outline: 1px solid #e50000; 33 | } 34 | 35 | .validation-message { 36 | color: #e50000; 37 | } 38 | 39 | .blazor-error-boundary { 40 | background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121; 41 | padding: 1rem 1rem 1rem 3.7rem; 42 | color: white; 43 | } 44 | 45 | .blazor-error-boundary::after { 46 | content: "An error has occurred." 47 | } 48 | 49 | .darker-border-checkbox.form-check-input { 50 | border-color: #929292; 51 | } 52 | -------------------------------------------------------------------------------- /BlazorWebApp/BlazorWebApp/wwwroot/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EdCharbeneau/BlazorSize/59af9c85463a6a675a91cfc957bd0a81f2dd8209/BlazorWebApp/BlazorWebApp/wwwroot/favicon.png -------------------------------------------------------------------------------- /PlaywrightTests/MediaQueryTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Playwright; 2 | 3 | namespace PlaywrightTests; 4 | 5 | [Parallelizable(ParallelScope.Self)] 6 | [TestFixture] 7 | public class MediaQueryTests : PageTest 8 | { 9 | [Test] 10 | public async Task MyNewPlaywrightTest() 11 | { 12 | // Act 13 | await Page.SetViewportSizeAsync(767, 800); 14 | await Page.GotoAsync(TestHelpers.GetUrl("/fetchdata")); 15 | 16 | // Assert 17 | await Expect(Page.GetByTestId("weather-card-0")).ToBeVisibleAsync(); 18 | await Expect(Page.GetByTestId("weather-grid")).Not.ToBeVisibleAsync(); 19 | 20 | // Act 21 | await Page.SetViewportSizeAsync(768, 1200); 22 | await Page.GotoAsync(TestHelpers.GetUrl("/fetchdata")); 23 | 24 | // Assert 25 | await Expect(Page.GetByTestId("weather-card-0")).ToBeVisibleAsync(); 26 | await Expect(Page.GetByTestId("weather-grid")).Not.ToBeVisibleAsync(); 27 | 28 | } 29 | } 30 | 31 | -------------------------------------------------------------------------------- /PlaywrightTests/PlaywrightTests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net9.0 5 | latest 6 | enable 7 | enable 8 | false 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | all 18 | runtime; build; native; contentfiles; analyzers; buildtransitive 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /PlaywrightTests/ResizeTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Playwright; 2 | 3 | namespace PlaywrightTests; 4 | 5 | [Parallelizable(ParallelScope.Self)] 6 | [TestFixture] 7 | public class Tests : PageTest 8 | { 9 | [Test] 10 | public async Task IndexPageLoadsWithBlazorSizeAndDisplaysInitialPageSizeOf1920By1080() 11 | // This test method verifies that BlazorSize is working by exercising the browser's resize method. It performs the following steps: 12 | // 1. Navigates to the index page of the Blazor application. 13 | // 2. Sets the viewport size to 1920 by 1080. 14 | // 3. Expects that the element with the title "browserWidth" contains the text "1920". 15 | // 4. Expects that the element with the title "browserHeight" contains the text "1080". 16 | // 5. Expects that the element with the title "isSmallMedia" contains the text "True". 17 | { 18 | 19 | await Page.GotoAsync(TestHelpers.GetUrl("/")); 20 | await Page.SetViewportSizeAsync(1080, 720); 21 | await Page.SetViewportSizeAsync(1920, 1080); 22 | await Expect(Page.GetByTitle("browserWidth")).ToContainTextAsync("1920"); 23 | await Expect(Page.GetByTitle("browserHeight")).ToContainTextAsync("1080"); 24 | await Expect(Page.GetByTitle("isSmallMedia")).ToContainTextAsync("True"); 25 | Page.PageError += (_, exception) => throw new Exception($"Page errors occurred {exception}"); 26 | } 27 | 28 | } 29 | 30 | -------------------------------------------------------------------------------- /PlaywrightTests/TestHelpers.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace PlaywrightTests 8 | { 9 | internal static class TestHelpers 10 | { 11 | public static string SUTUrlTemplate { get; } = "https://localhost:7249{0}"; 12 | public static string GetUrl(string route) => string.Format(SUTUrlTemplate, route); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /TestComponents/Data/IWeatherForecastService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | 5 | namespace TestComponents 6 | { 7 | public interface IWeatherForecastService 8 | { 9 | Task GetForecastAsync(DateTime startDate); 10 | } 11 | 12 | public class WeatherForecastService : IWeatherForecastService 13 | { 14 | private static readonly string[] Summaries = new[] 15 | { 16 | "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" 17 | }; 18 | 19 | public Task GetForecastAsync(DateTime startDate) 20 | { 21 | var rng = new Random(); 22 | return Task.FromResult(Enumerable.Range(1, 5).Select(index => new WeatherForecast 23 | { 24 | Date = startDate.AddDays(index), 25 | TemperatureC = rng.Next(-20, 55), 26 | Summary = Summaries[rng.Next(Summaries.Length)] 27 | }).ToArray()); 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /TestComponents/Pages/Blank.razor: -------------------------------------------------------------------------------- 1 | @page "/blank" 2 |

Blank

3 | 4 | @code { 5 | 6 | } 7 | -------------------------------------------------------------------------------- /TestComponents/Pages/Error.razor: -------------------------------------------------------------------------------- 1 | @page "/error" 2 | 3 | 4 |

Error.

5 |

An error occurred while processing your request.

6 | 7 |

Development Mode

8 |

9 | Swapping to Development environment will display more detailed information about the error that occurred. 10 |

11 |

12 | The Development environment shouldn't be enabled for deployed applications. 13 | It can result in displaying sensitive information from exceptions to end users. 14 | For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development 15 | and restarting the app. 16 |

-------------------------------------------------------------------------------- /TestComponents/Pages/FetchData.razor: -------------------------------------------------------------------------------- 1 | @inject IWeatherForecastService Forecasts 2 | @using TestComponents 3 | @page "/fetchdata" 4 | 5 |

Weather forecast

6 | 7 |

This component demonstrates adaptive rendering of a Blazor UI.

8 | 9 | @if (IsSmall) 10 | { 11 | 12 | } 13 | else 14 | { 15 | 16 | } 17 | 18 | @if (IsMedium) 19 | { 20 | Medium 21 | } 22 | 23 | 24 | 25 | 26 | @code { 27 | WeatherForecast[] forecasts; 28 | 29 | bool IsMedium = false; 30 | bool IsSmall = false; 31 | 32 | protected override async Task OnInitializedAsync() 33 | { 34 | forecasts = await Forecasts.GetForecastAsync(DateTime.Now); 35 | } 36 | } 37 | 38 | 39 | -------------------------------------------------------------------------------- /TestComponents/Pages/Index.razor: -------------------------------------------------------------------------------- 1 | @implements IDisposable 2 | @inject IResizeListener listener 3 | @page "/" 4 | 5 |

Height: @browser.Height

6 |

Width: @browser.Width

7 |

MQ: @IsSmallMedia

8 | 9 | @code { 10 | 11 | BrowserWindowSize browser = new BrowserWindowSize(); 12 | 13 | bool IsSmallMedia = false; 14 | 15 | 16 | protected override void OnAfterRender(bool firstRender) 17 | { 18 | if (firstRender) 19 | { 20 | listener.OnResized += WindowResizedA; 21 | } 22 | } 23 | 24 | void IDisposable.Dispose() 25 | { 26 | listener.OnResized -= WindowResizedA; 27 | } 28 | 29 | async void WindowResizedA(object _, BrowserWindowSize window) 30 | { 31 | browser = window; 32 | IsSmallMedia = await listener.MatchMedia(Breakpoints.XLargeUp); 33 | StateHasChanged(); 34 | } 35 | 36 | 37 | } -------------------------------------------------------------------------------- /TestComponents/Pages/Multicast.razor: -------------------------------------------------------------------------------- 1 | @page "/multicast" 2 |

Multicast

3 |
4 |
5 | 6 |
7 |
8 | 9 |
10 |
11 | 12 |
13 |
-------------------------------------------------------------------------------- /TestComponents/Pages/NullableFlag.razor: -------------------------------------------------------------------------------- 1 | @page "/nullable" 2 | @if (IsSmall.HasValue) 3 | { 4 | if (IsSmall.Value) 5 | { 6 |

Small

7 | } 8 | else 9 | { 10 |

Large

11 | } 12 | } else 13 | { 14 | Waiting for JavaScript interop... 15 | } 16 | 17 | 18 | 19 | @code { 20 | bool? IsSmall = null; 21 | } 22 | -------------------------------------------------------------------------------- /TestComponents/Pages/ScreenSize.razor: -------------------------------------------------------------------------------- 1 | @page "/size" 2 | 3 | 4 | @code { 5 | 6 | } -------------------------------------------------------------------------------- /TestComponents/Pages/ScreenSizeReporter.razor: -------------------------------------------------------------------------------- 1 | 
Device Size
2 |
    3 |
  • Large
  • 4 |
  • Medium
  • 5 |
  • Small
  • 6 |
7 | 8 | 9 | 10 | 11 | 12 | @code { 13 | Func Active = state => state ? "active" : ""; 14 | bool IsLarge = false; 15 | bool IsMedium = false; 16 | bool IsSmall = false; 17 | } 18 | -------------------------------------------------------------------------------- /TestComponents/Pages/Templates.razor: -------------------------------------------------------------------------------- 1 | @inject IWeatherForecastService Forecasts 2 | 3 | @using TestComponents 4 | @page "/templates" 5 | 6 |

Weather forecast

7 | 8 |

This component demonstrates adaptive rendering of a Blazor UI.

9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | @code { 21 | WeatherForecast[] forecasts; 22 | 23 | protected override async Task OnInitializedAsync() 24 | { 25 | forecasts = await Forecasts.GetForecastAsync(DateTime.Now); 26 | } 27 | } -------------------------------------------------------------------------------- /TestComponents/TestComponents.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net9.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /TestComponents/WeatherCards.razor: -------------------------------------------------------------------------------- 1 | @if (Data == null) 2 | { 3 |

Loading...

4 | } 5 | else 6 | { 7 | foreach (var t in Data.Select((x,i) => new { Forecast = x, Index = i })) 8 | { 9 |
10 |
11 |
@t.Forecast.Date.DayOfWeek
12 |
@t.Forecast.Date.ToShortDateString()
13 |

14 | @t.Forecast.TemperatureC (C) / @t.Forecast.TemperatureF (F) 15 |

16 |

17 | @t.Forecast.Summary 18 |

19 |
20 |
21 | } 22 | } 23 | 24 | @code { 25 | [Parameter] public WeatherForecast[] Data { get; set; } 26 | } -------------------------------------------------------------------------------- /TestComponents/WeatherForecast.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | namespace TestComponents 4 | { 5 | public class WeatherForecast 6 | { 7 | public DateTime Date { get; set; } 8 | 9 | public int TemperatureC { get; set; } 10 | 11 | public string Summary { get; set; } 12 | 13 | public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /TestComponents/WeatherGrid.razor: -------------------------------------------------------------------------------- 1 | @if (Data == null) 2 | { 3 |

Loading...

4 | } 5 | else 6 | { 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | @foreach (var forecast in Data) 18 | { 19 | 20 | 21 | 22 | 23 | 24 | 25 | } 26 | 27 |
DateTemp. (C)Temp. (F)Summary
@forecast.Date.ToShortDateString()@forecast.TemperatureC@forecast.TemperatureF@forecast.Summary
28 | } 29 | 30 | @code { 31 | [Parameter] public WeatherForecast[] Data { get; set; } 32 | } -------------------------------------------------------------------------------- /TestComponents/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using Microsoft.AspNetCore.Components.Web 2 | @using BlazorPro.BlazorSize -------------------------------------------------------------------------------- /TestComponents/wwwroot/fixture.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |

Responsive test fixture

10 | 11 | -------------------------------------------------------------------------------- /TestComponents/wwwroot/fixture.js: -------------------------------------------------------------------------------- 1 | var myWindow; 2 | 3 | function openWin(page, w, h) { 4 | myWindow = window.open(`/${page}`, "", `width=${w}, height=${h}`); 5 | } 6 | 7 | function resizeWin(w, h) { 8 | myWindow.resizeTo(w, h); 9 | } -------------------------------------------------------------------------------- /_resources/blazor-glove-128.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EdCharbeneau/BlazorSize/59af9c85463a6a675a91cfc957bd0a81f2dd8209/_resources/blazor-glove-128.jpg -------------------------------------------------------------------------------- /_sponsors/Telerik/Blazor-300x250.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EdCharbeneau/BlazorSize/59af9c85463a6a675a91cfc957bd0a81f2dd8209/_sponsors/Telerik/Blazor-300x250.png -------------------------------------------------------------------------------- /_sponsors/Telerik/blazor-300x250_v2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EdCharbeneau/BlazorSize/59af9c85463a6a675a91cfc957bd0a81f2dd8209/_sponsors/Telerik/blazor-300x250_v2.png -------------------------------------------------------------------------------- /_sponsors/Telerik/blazor-300x600.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EdCharbeneau/BlazorSize/59af9c85463a6a675a91cfc957bd0a81f2dd8209/_sponsors/Telerik/blazor-300x600.png -------------------------------------------------------------------------------- /_sponsors/Telerik/blazor-300x600_v2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EdCharbeneau/BlazorSize/59af9c85463a6a675a91cfc957bd0a81f2dd8209/_sponsors/Telerik/blazor-300x600_v2.png -------------------------------------------------------------------------------- /bUnitCompatibilityTests/CompTest.cs: -------------------------------------------------------------------------------- 1 | using BlazorPro.BlazorSize; 2 | using Bunit; 3 | using Xunit; 4 | 5 | namespace bUnitCompatibilityTests 6 | { 7 | public class CompTest 8 | { 9 | [Fact] 10 | public void Shows_Correct_Content_Based_On_MediaQuery() 11 | { 12 | // Arrange 13 | using var ctx = new TestContext(); 14 | var blazorSizeState = ctx.AddBlazorSize(); 15 | blazorSizeState.SetActiveBreakPoint(Breakpoints.LargeDown); 16 | 17 | // Act 18 | var cut = ctx.RenderComponent(); 19 | 20 | // Assert 21 | cut.MarkupMatches("DOES NOT MATCH"); 22 | 23 | // Change breakpoint 24 | blazorSizeState.SetActiveBreakPoint(Breakpoints.SmallDown); 25 | 26 | // Assert 27 | cut.MarkupMatches("MATCHES"); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /bUnitCompatibilityTests/Sample.razor: -------------------------------------------------------------------------------- 1 | @using BlazorPro.BlazorSize 2 | 3 | 4 | MATCHES 5 | 6 | 7 | DOES NOT MATCH 8 | 9 | -------------------------------------------------------------------------------- /bUnitCompatibilityTests/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using Microsoft.AspNetCore.Components.Forms 2 | @using Microsoft.AspNetCore.Components.Web 3 | @using Microsoft.JSInterop 4 | @using Microsoft.Extensions.DependencyInjection 5 | @using AngleSharp.Dom 6 | @using Bunit 7 | @using Bunit.TestDoubles 8 | @using Xunit -------------------------------------------------------------------------------- /bUnitCompatibilityTests/bUnitCompatibilityTests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net9.0 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | runtime; build; native; contentfiles; analyzers; buildtransitive 15 | all 16 | 17 | 18 | runtime; build; native; contentfiles; analyzers; buildtransitive 19 | all 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "BlazorSize", 3 | "lockfileVersion": 3, 4 | "requires": true, 5 | "packages": { 6 | "": { 7 | "devDependencies": { 8 | "@playwright/test": "^1.47.1" 9 | } 10 | }, 11 | "node_modules/@playwright/test": { 12 | "version": "1.47.1", 13 | "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.47.1.tgz", 14 | "integrity": "sha512-dbWpcNQZ5nj16m+A5UNScYx7HX5trIy7g4phrcitn+Nk83S32EBX/CLU4hiF4RGKX/yRc93AAqtfaXB7JWBd4Q==", 15 | "dev": true, 16 | "dependencies": { 17 | "playwright": "1.47.1" 18 | }, 19 | "bin": { 20 | "playwright": "cli.js" 21 | }, 22 | "engines": { 23 | "node": ">=18" 24 | } 25 | }, 26 | "node_modules/fsevents": { 27 | "version": "2.3.2", 28 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 29 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 30 | "dev": true, 31 | "hasInstallScript": true, 32 | "optional": true, 33 | "os": [ 34 | "darwin" 35 | ], 36 | "engines": { 37 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 38 | } 39 | }, 40 | "node_modules/playwright": { 41 | "version": "1.47.1", 42 | "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.47.1.tgz", 43 | "integrity": "sha512-SUEKi6947IqYbKxRiqnbUobVZY4bF1uu+ZnZNJX9DfU1tlf2UhWfvVjLf01pQx9URsOr18bFVUKXmanYWhbfkw==", 44 | "dev": true, 45 | "dependencies": { 46 | "playwright-core": "1.47.1" 47 | }, 48 | "bin": { 49 | "playwright": "cli.js" 50 | }, 51 | "engines": { 52 | "node": ">=18" 53 | }, 54 | "optionalDependencies": { 55 | "fsevents": "2.3.2" 56 | } 57 | }, 58 | "node_modules/playwright-core": { 59 | "version": "1.47.1", 60 | "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.47.1.tgz", 61 | "integrity": "sha512-i1iyJdLftqtt51mEk6AhYFaAJCDx0xQ/O5NU8EKaWFgMjItPVma542Nh/Aq8aLCjIJSzjaiEQGW/nyqLkGF1OQ==", 62 | "dev": true, 63 | "bin": { 64 | "playwright-core": "cli.js" 65 | }, 66 | "engines": { 67 | "node": ">=18" 68 | } 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "devDependencies": { 3 | "@playwright/test": "^1.47.1" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | BlazorSize is a JavaScript interop library for Blazor that is used to detect the Browser's current size, change in size, and test media queries. 2 | 3 | BlazorSize was designed to allow Razor Components to implement adaptive rendering. Handle mobile/desktop rendering changes at runtime with ease and complement your apps CSS media queries with programmatic work flows. 4 | 5 | [For documentation please refer to the Wiki section of this GitHub repo.](https://github.com/EdCharbeneau/BlazorSize/wiki) 6 | 7 |
8 | 9 | A component with similar functionality of BlazorSize can also be found in [Telerik UI for Blazor](https://demos.telerik.com/blazor-ui/mediaquery/overview?utm_source=EdCharbeneau&utm_medium=cpm&utm_campaign=blazor-github-sponsored-message). 10 | 11 | [![Telerik UI for Blazor, Increase productivity and cut costs in half. Try Now](https://github.com/EdCharbeneau/BlazorSize/blob/master/_sponsors/Telerik/Blazor-300x250.png?raw=true)]((https://www.telerik.com/campaigns/blazor/free-trial-1?utm_source=EdCharbeneau&utm_medium=cpm&utm_campaign=blazor-github-sponsored-message)) 12 | 13 | **If you’re already a Telerik customer there’s no need to use BlazorSize**, instead you may use the TelerikMediaQuery component. If you’re looking for a full component library of 100+ components, world class support, and rich documentation then [give Telerik UI for Blazor a try for free](https://www.telerik.com/campaigns/blazor/free-trial-1?utm_source=EdCharbeneau&utm_medium=cpm&utm_campaign=blazor-github-sponsored-message). 14 | --------------------------------------------------------------------------------