async Task Save()
        {
            testSpec.SpinnerSaving       = true;
            testSpec.SaveLabel           = "Wait";
            testSpec.ControlsDisabled    = true;
            testSpec.TenorWeightDisabled = true;
            screen.PlayDisabled          = true;
            blowSet.Blows.Last().BellColor = Constants.DisabledUnstruckTestBellColor;
            StateHasChanged();

            // Push the created test to the API in JSON format
            // Start by creating a BlowSetCore object, which just has the parent data BlowSet
            // Note implicit cast from child to parent
            BlowSetCore blowSetCore = blowSet;

            // BlowSetCore has a BlowsCore list which is empty so far
            // Call the LoadBlowsCore method to populate it
            blowSetCore.LoadBlowsCore(blowSet);

            // Next use the Serializer method of the JsonSerializer class (in the System.Text.Json namespace) to create
            // a Json object from the BlowSetData object
            var gapTestData = new GapTestData
            {
                GapTestSpec = JsonSerializer.Serialize(blowSetCore)
            };

            // Push the Json object to the API
            await TJBarnesService.GetHttpClient().PostAsJsonAsync("api/gaptests", gapTestData);

            // Refresh the contents of the Select Test dropdown
            gapTestsData = (await TJBarnesService.GetHttpClient()
                            .GetFromJsonAsync <GapTestData[]>("api/gaptests")).ToList();

            testSpec.SpinnerSaving = false;
            testSpec.Saved         = true;
            StateHasChanged();

            await Task.Delay(1000);

            testSpec.Saved               = false;
            testSpec.SaveLabel           = "Save";
            testSpec.ControlsDisabled    = false;
            testSpec.TenorWeightDisabled = TenorWeightSelect.TenorWeightDisabled(testSpec.Stage);
            screen.PlayDisabled          = false;
            blowSet.Blows.Last().BellColor = Constants.UnstruckTestBellColor;
            StateHasChanged();
        }
        async Task Load(int id)
        {
            // Get a test from the API
            GapTestData gapTestData = await TJBarnesService.GetHttpClient().
                                      GetFromJsonAsync <GapTestData>("api/gaptests/" + id.ToString());

            // Use the Deserializer method of the JsonSerializer class (in the System.Text.Json namespace) to create
            // a BlowSetCore object
            BlowSetCore blowSetCore = JsonSerializer.Deserialize <BlowSetCore>(gapTestData.GapTestSpec);

            // Now create a BlowSet object from the BlowSetCore object
            blowSet = new BlowSet(blowSetCore.Stage, blowSetCore.NumRows, blowSetCore.TenorWeight,
                                  blowSetCore.ErrorType, true);

            // No need for an audio suffix in a Gap test (this is used to distinguish A and B in an A/B test)
            blowSet.LoadBlows(blowSetCore, string.Empty);
            blowSet.SetUnstruck();

            // Update drop down boxes on screen
            testSpec.Stage       = blowSet.Stage;
            testSpec.TenorWeight = blowSet.TenorWeight;
            testSpec.NumRows     = blowSet.NumRows;

            // Set up test spec-dependent elements of the screen object
            int baseGap = BaseGaps.BaseGap(testSpec.Stage, testSpec.TenorWeight, 1);

            testSpec.BaseGap = baseGap;
            testSpec.GapMin  = 20;

            // If test bell is 1st's place of a handstroke row, need to adjust GapMax to have a higher value
            // because of the handstroke gap
            if (blowSet.Blows.Last().IsHandstroke == true && blowSet.Blows.Last().Place == 1)
            {
                testSpec.GapMax = Convert.ToInt32(Math.Round(((double)testSpec.BaseGap * 3) / 50)) * 50;
            }
            else
            {
                testSpec.GapMax = Convert.ToInt32(Math.Round(((double)baseGap * 2) / 50)) * 50;
            }

            testSpec.ShowGaps = false;
            StateHasChanged();
        }