Esempio n. 1
0
 public Task SetParametersAsync(ParameterView parameters)
 => throw new NotImplementedException();
Esempio n. 2
0
 /// <inheritdoc />
 public Task SetParametersAsync(ParameterView parameters)
 {
     parameters.SetParameterProperties(this);
     Render();
     return(Task.CompletedTask);
 }
Esempio n. 3
0
        static CascadingValue <T> CreateCascadingValueComponent <T>(T value, string name = null)
        {
            var supplier = new CascadingValue <T>();
            var renderer = new TestRenderer();

            supplier.Attach(new RenderHandle(renderer, 0));

            var supplierParams = new Dictionary <string, object>
            {
                { "Value", value }
            };

            if (name != null)
            {
                supplierParams.Add("Name", name);
            }

            renderer.Dispatcher.InvokeAsync((Action)(() => supplier.SetParametersAsync(ParameterView.FromDictionary(supplierParams))));
            return(supplier);
        }
Esempio n. 4
0
        /// <inheritdoc />
        public Task SetParametersAsync(ParameterView parameters)
        {
            // Implementing the parameter binding manually, instead of just calling
            // parameters.SetParameterProperties(this), is just a very slight perf optimization
            // and makes it simpler impose rules about the params being required or not.

            var hasSuppliedValue = false;
            var previousValue    = Value;
            var previousFixed    = IsFixed;

            Value        = default;
            ChildContent = null;
            Name         = null;
            IsFixed      = false;

            foreach (var parameter in parameters)
            {
                if (parameter.Name.Equals(nameof(Value), StringComparison.OrdinalIgnoreCase))
                {
                    Value            = (TValue)parameter.Value;
                    hasSuppliedValue = true;
                }
                else if (parameter.Name.Equals(nameof(ChildContent), StringComparison.OrdinalIgnoreCase))
                {
                    ChildContent = (RenderFragment)parameter.Value;
                }
                else if (parameter.Name.Equals(nameof(Name), StringComparison.OrdinalIgnoreCase))
                {
                    Name = (string)parameter.Value;
                    if (string.IsNullOrEmpty(Name))
                    {
                        throw new ArgumentException($"The parameter '{nameof(Name)}' for component '{nameof(CascadingValue<TValue>)}' does not allow null or empty values.");
                    }
                }
                else if (parameter.Name.Equals(nameof(IsFixed), StringComparison.OrdinalIgnoreCase))
                {
                    IsFixed = (bool)parameter.Value;
                }
                else
                {
                    throw new ArgumentException($"The component '{nameof(CascadingValue<TValue>)}' does not accept a parameter with the name '{parameter.Name}'.");
                }
            }

            if (_hasSetParametersPreviously && IsFixed != previousFixed)
            {
                throw new InvalidOperationException($"The value of {nameof(IsFixed)} cannot be changed dynamically.");
            }

            _hasSetParametersPreviously = true;

            // It's OK for the value to be null, but some "Value" param must be supplied
            // because it serves no useful purpose to have a <CascadingValue> otherwise.
            if (!hasSuppliedValue)
            {
                throw new ArgumentException($"Missing required parameter '{nameof(Value)}' for component '{GetType().Name}'.");
            }

            // Rendering is most efficient when things are queued from rootmost to leafmost.
            // Given a components A (parent) -> B (child), you want them to be queued in order
            // [A, B] because if you had [B, A], then the render for A might change B's params
            // making it render again, so you'd render [B, A, B], which is wasteful.
            // At some point we might consider making the render queue actually enforce this
            // ordering during insertion.
            //
            // For the CascadingValue component, this observation is why it's important to render
            // ourself before notifying subscribers (which can be grandchildren or deeper).
            // If we rerendered subscribers first, then our own subsequent render might cause an
            // further update that makes those nested subscribers get rendered twice.
            _renderHandle.Render(Render);

            if (_subscribers != null && ChangeDetection.MayHaveChanged(previousValue, Value))
            {
                NotifySubscribers();
            }

            return(Task.CompletedTask);
        }
Esempio n. 5
0
 public Task SetParametersAsync(ParameterView parameters)
 {
     throw new System.NotImplementedException();
 }
        public static (IList <ComponentParameter> parameterDefinitions, IList <object> parameterValues) FromParameterView(ParameterView parameters)
        {
            var parameterDefinitions = new List <ComponentParameter>();
            var parameterValues      = new List <object>();

            foreach (var kvp in parameters)
            {
                var valueType = kvp.Value?.GetType();
                parameterDefinitions.Add(new ComponentParameter
                {
                    Name     = kvp.Name,
                    TypeName = valueType?.FullName,
                    Assembly = valueType?.Assembly?.GetName()?.Name
                });

                parameterValues.Add(kvp.Value);
            }

            return(parameterDefinitions, parameterValues);
        }
Esempio n. 7
0
 public override Task SetParametersAsync(ParameterView parameters) => base.SetParametersAsync(parameters);
Esempio n. 8
0
        // It's internal because there isn't a known use case for user code comparing
        // ParameterView instances, and even if there was, it's unlikely it should
        // use these equality rules which are designed for their effect on rendering.
        internal bool DefinitelyEquals(ParameterView oldParameters)
        {
            // In general we can't detect mutations on arbitrary objects. We can't trust
            // things like .Equals or .GetHashCode because they usually only tell us about
            // shallow changes, not deep mutations. So we return false if both:
            //  [1] All the parameters are known to be immutable (i.e., Type.IsPrimitive
            //      or is in a known set of common immutable types)
            //  [2] And all the parameter values are equal to their previous values
            // Otherwise be conservative and return false.
            // To make this check cheaper, since parameters are virtually always generated in
            // a deterministic order, we don't bother to account for reordering, so if any
            // of the names don't match sequentially we just return false too.
            //
            // The logic here may look kind of epic, and would certainly be simpler if we
            // used ParameterEnumerator.GetEnumerator(), but it's perf-critical and this
            // implementation requires a lot fewer instructions than a GetEnumerator-based one.

            var oldIndex        = oldParameters._ownerIndex;
            var newIndex        = _ownerIndex;
            var oldEndIndexExcl = oldIndex + oldParameters._frames[oldIndex].ComponentSubtreeLength;
            var newEndIndexExcl = newIndex + _frames[newIndex].ComponentSubtreeLength;

            while (true)
            {
                // First, stop if we've reached the end of either subtree
                oldIndex++;
                newIndex++;
                var oldFinished = oldIndex == oldEndIndexExcl;
                var newFinished = newIndex == newEndIndexExcl;
                if (oldFinished || newFinished)
                {
                    return(oldFinished == newFinished); // Same only if we have same number of parameters
                }
                else
                {
                    // Since neither subtree has finished, it's safe to read the next frame from both
                    ref var oldFrame = ref oldParameters._frames[oldIndex];
                    ref var newFrame = ref _frames[newIndex];

                    // Stop if we've reached the end of either subtree's sequence of attributes
                    oldFinished = oldFrame.FrameType != RenderTreeFrameType.Attribute;
                    newFinished = newFrame.FrameType != RenderTreeFrameType.Attribute;
                    if (oldFinished || newFinished)
                    {
                        return(oldFinished == newFinished); // Same only if we have same number of parameters
                    }
                    else
                    {
                        if (!string.Equals(oldFrame.AttributeName, newFrame.AttributeName, StringComparison.Ordinal))
                        {
                            return(false); // Different names
                        }

                        var oldValue = oldFrame.AttributeValue;
                        var newValue = newFrame.AttributeValue;
                        if (ChangeDetection.MayHaveChanged(oldValue, newValue))
                        {
                            return(false);
                        }
                    }
                }
 public Task SetParametersAsync(ParameterView parameters)
 {
     parameters.SetParameterProperties(this);
     _registry.SetContent(Name, ChildContent);
     return(Task.CompletedTask);
 }
Esempio n. 10
0
 public override Task SetParametersAsync(ParameterView parameters)
 {
     ReplaceChildContent(ref parameters);
     return(base.SetParametersAsync(parameters));
 }
Esempio n. 11
0
 internal static RenderTreeFrame[] GetFrames(ParameterView parameters)
 => (RenderTreeFrame[])_framesField.GetValue(parameters);
Esempio n. 12
0
        public void UpdatesOutputWhenRouteDataChanges()
        {
            // Arrange/Act 1: Start on some route
            // Not asserting about the initial output, as that is covered by other tests
            var routeData = new RouteData(typeof(TestPageWithNoAuthorization), EmptyParametersDictionary);

            _renderer.RenderRootComponent(_authorizeRouteViewComponentId, ParameterView.FromDictionary(new Dictionary <string, object>
            {
                { nameof(AuthorizeRouteView.RouteData), routeData },
                { nameof(AuthorizeRouteView.DefaultLayout), typeof(TestLayout) },
            }));

            // Act 2: Move to another route
            var routeData2  = new RouteData(typeof(TestPageRequiringAuthorization), EmptyParametersDictionary);
            var render2Task = _renderer.Dispatcher.InvokeAsync(() => _authorizeRouteViewComponent.SetParametersAsync(ParameterView.FromDictionary(new Dictionary <string, object>
            {
                { nameof(AuthorizeRouteView.RouteData), routeData2 },
            })));

            // Assert: we retain the layout instance, and mutate its contents
            Assert.True(render2Task.IsCompletedSuccessfully);
            Assert.Equal(2, _renderer.Batches.Count);
            var batch2 = _renderer.Batches[1];
            var diff   = batch2.DiffsInOrder.Where(d => d.Edits.Any()).Single();

            Assert.Collection(diff.Edits,
                              edit =>
            {
                // Inside the layout, we add the new content
                Assert.Equal(RenderTreeEditType.PrependFrame, edit.Type);
                Assert.Equal(1, edit.SiblingIndex);
                AssertFrame.Text(batch2.ReferenceFrames[edit.ReferenceFrameIndex], "Not authorized");
            },
                              edit =>
            {
                // ... and remove the old content
                Assert.Equal(RenderTreeEditType.RemoveFrame, edit.Type);
                Assert.Equal(2, edit.SiblingIndex);
            });
        }
Esempio n. 13
0
        public void WhenAuthorizing_RendersCustomAuthorizingContentInsideLayout()
        {
            // Arrange
            var routeData    = new RouteData(typeof(TestPageRequiringAuthorization), EmptyParametersDictionary);
            var authStateTcs = new TaskCompletionSource <AuthenticationState>();

            _authenticationStateProvider.CurrentAuthStateTask = authStateTcs.Task;
            RenderFragment customAuthorizing =
                builder => builder.AddContent(0, "Hold on, we're checking your papers.");

            // Act
            var firstRenderTask = _renderer.RenderRootComponentAsync(_authorizeRouteViewComponentId, ParameterView.FromDictionary(new Dictionary <string, object>
            {
                { nameof(AuthorizeRouteView.RouteData), routeData },
                { nameof(AuthorizeRouteView.DefaultLayout), typeof(TestLayout) },
                { nameof(AuthorizeRouteView.Authorizing), customAuthorizing },
            }));

            // Assert: renders layout containing "authorizing" message
            Assert.False(firstRenderTask.IsCompleted);
            var batch      = _renderer.Batches.Single();
            var layoutDiff = batch.GetComponentDiffs <TestLayout>().Single();

            Assert.Collection(layoutDiff.Edits,
                              edit => AssertPrependText(batch, edit, "Layout starts here"),
                              edit => AssertPrependText(batch, edit, "Hold on, we're checking your papers."),
                              edit => AssertPrependText(batch, edit, "Layout ends here"));
        }
Esempio n. 14
0
        public async Task WhenAuthorizing_RendersDefaultAuthorizingContentInsideLayout()
        {
            // Arrange
            var routeData    = new RouteData(typeof(TestPageRequiringAuthorization), EmptyParametersDictionary);
            var authStateTcs = new TaskCompletionSource <AuthenticationState>();

            _authenticationStateProvider.CurrentAuthStateTask = authStateTcs.Task;
            RenderFragment <AuthenticationState> customNotAuthorized =
                state => builder => builder.AddContent(0, $"Go away, {state.User.Identity.Name}");

            // Act
            var firstRenderTask = _renderer.RenderRootComponentAsync(_authorizeRouteViewComponentId, ParameterView.FromDictionary(new Dictionary <string, object>
            {
                { nameof(AuthorizeRouteView.RouteData), routeData },
                { nameof(AuthorizeRouteView.DefaultLayout), typeof(TestLayout) },
                { nameof(AuthorizeRouteView.NotAuthorized), customNotAuthorized },
            }));

            // Assert: renders layout containing "authorizing" message
            Assert.False(firstRenderTask.IsCompleted);
            var batch      = _renderer.Batches.Single();
            var layoutDiff = batch.GetComponentDiffs <TestLayout>().Single();

            Assert.Collection(layoutDiff.Edits,
                              edit => AssertPrependText(batch, edit, "Layout starts here"),
                              edit => AssertPrependText(batch, edit, "Authorizing..."),
                              edit => AssertPrependText(batch, edit, "Layout ends here"));

            // Act 2: updates when authorization completes
            authStateTcs.SetResult(new AuthenticationState(
                                       new ClaimsPrincipal(new TestIdentity {
                Name = "Bert"
            })));
            await firstRenderTask;

            // Assert 2: Only the layout is updated
            batch = _renderer.Batches.Skip(1).Single();
            var nonEmptyDiff = batch.DiffsInOrder.Where(d => d.Edits.Any()).Single();

            Assert.Equal(layoutDiff.ComponentId, nonEmptyDiff.ComponentId);
            Assert.Collection(nonEmptyDiff.Edits, edit =>
            {
                Assert.Equal(RenderTreeEditType.UpdateText, edit.Type);
                Assert.Equal(1, edit.SiblingIndex);
                AssertFrame.Text(batch.ReferenceFrames[edit.ReferenceFrameIndex], "Go away, Bert");
            });
        }