[Perf] Disabled Test Picker fields while waiting for requests.

This prevents undefined behavior that occurred when a user modified a field while the next one was still loading.

Additionally add demo page for ExploreMultiSk.

Change-Id: I28b1a4f2c9a23a2e30079dee979b74fb63781e1a
Reviewed-on: https://skia-review.googlesource.com/c/buildbot/+/868649
Reviewed-by: Ashwin Verleker <ashwinpv@google.com>
Commit-Queue: Eduardo Yap <eduardoyap@google.com>
diff --git a/perf/modules/common/BUILD.bazel b/perf/modules/common/BUILD.bazel
index b8a0c4c..78cfd94 100644
--- a/perf/modules/common/BUILD.bazel
+++ b/perf/modules/common/BUILD.bazel
@@ -19,3 +19,14 @@
         "//perf/modules/json:index_ts_lib",
     ],
 )
+
+ts_library(
+    name = "test-util_ts_lib",
+    srcs = ["test-util.ts"],
+    visibility = ["//visibility:public"],
+    deps = [
+        "//:node_modules/fetch-mock",
+        "//infra-sk/modules/json:index_ts_lib",
+        "//perf/modules/json:index_ts_lib",
+    ],
+)
diff --git a/perf/modules/common/test-util.ts b/perf/modules/common/test-util.ts
new file mode 100644
index 0000000..fdd7c2f
--- /dev/null
+++ b/perf/modules/common/test-util.ts
@@ -0,0 +1,435 @@
+// Functions to be used by test and demo files. These helps create dummy data and API mocks.
+import fetchMock, { restore } from 'fetch-mock';
+import { Status } from '../../../infra-sk/modules/json';
+import { QueryConfig, GraphConfig, ParamSet } from '../json';
+
+export function setUpExploreDemoEnv() {
+  const status: Status = {
+    email: 'user@google.com',
+    roles: ['viewer', 'admin', 'editor', 'bisecter'],
+  };
+
+  fetchMock.get('/_/login/status', status);
+
+  const paramSet = {
+    arch: ['arm', 'arm64', 'x86_64'],
+    bench_type: ['skandroidcodec'],
+    compiler: ['Clang'],
+    config: ['nonrendering'],
+    configuration: ['OptimizeForSize'],
+    cpu_or_gpu: ['CPU'],
+    cpu_or_gpu_value: ['AVX2', 'AVX512', 'Snapdragon855', 'SnapdragonQM215'],
+    extra_config: [
+      'Android',
+      'Android_Wuffs',
+      'ColorSpaces',
+      'Fast',
+      'SK_FORCE_RASTER_PIPELINE_BLITTER',
+      'Wuffs',
+    ],
+    model: ['GCE', 'JioNext', 'MacBookPro11.5', 'NUC9i7QN', 'Pixel4'],
+    name: ['AndroidCodec_01_original.jpg_SampleSize2'],
+    os: ['Android', 'Debian10', 'Debian11', 'Mac10.13', 'Win2019'],
+    source_type: ['image'],
+    sub_result: ['min_ms', 'min_ratio'],
+  };
+
+  fetchMock.get(/_\/initpage\/.*/, () => ({
+    dataframe: {
+      traceset: null,
+      header: null,
+      paramset: paramSet,
+      skip: 0,
+    },
+    ticks: [],
+    skps: [],
+    msg: '',
+  }));
+
+  fetchMock.post('/_/count/', {
+    count: 117, // Don't make the demo page non-deterministic.
+    paramset: paramSet,
+  });
+
+  fetchMock.post('/_/frame/start', {
+    status: 'Running',
+    messages: [],
+    url: '/_/status/d25fedcc-7e36-47e4-83d5-58ab76b2d3d1',
+  });
+
+  const defaultConfig: QueryConfig = {
+    default_param_selections: null,
+    default_url_values: null,
+    include_params: [
+      'arch',
+      'config',
+      'bench_type',
+      'compiler',
+      'model',
+      'os',
+      'sub_result',
+    ],
+  };
+
+  fetchMock.get('/_/defaults/', defaultConfig);
+
+  const normalTracesResponse = {
+    status: 'Finished',
+    messages: [
+      {
+        key: 'Step',
+        value: '1/1',
+      },
+    ],
+    results: {
+      dataframe: {
+        traceset: {
+          ',arch=arm,bench_type=skandroidcodec,compiler=Clang,config=nonrendering,cpu_or_gpu=CPU,cpu_or_gpu_value=SnapdragonQM215,extra_config=Android,model=JioNext,name=AndroidCodec_01_original.jpg_SampleSize2,os=Android,source_type=image,sub_result=min_ms,test=AndroidCodec_01_original.jpg_SampleSize2_640_480,':
+            [
+              61.2075, 60.687603, 61.30078, 61.660313, 60.830208, 60.854946,
+              60.8525, 61.43297, 61.24557, 61.098125, 61.284843, 60.7938,
+              61.741615, 62.60328, 60.93729, 60.925156, 63.232346, 61.770676,
+              62.252968, 61.87958, 61.140102, 62.40708, 62.869167, 60.893852,
+              61.042187, 61.17974, 61.73057, 61.754063, 60.726772, 61.837135,
+              61.868282, 61.161095, 61.88469, 60.81271, 61.4625, 60.91443,
+              60.806095, 60.81344, 61.624477, 60.98828, 60.838856, 61.989845,
+              60.84349, 61.973698, 61.97073, 60.615208, 62.083595, 61.148228,
+              1e32, 1e32,
+            ],
+          ',arch=arm,bench_type=skandroidcodec,compiler=Clang,config=nonrendering,cpu_or_gpu=CPU,cpu_or_gpu_value=SnapdragonQM215,extra_config=Android,model=JioNext,name=AndroidCodec_01_original.jpg_SampleSize2,os=Android,source_type=image,sub_result=min_ratio,test=AndroidCodec_01_original.jpg_SampleSize2_640_480,':
+            [
+              1.0053873, 1.0019164, 1.0029848, 1.002643, 1.0012664, 1.0028929,
+              1.0037411, 1.003003, 1.0050658, 1.007233, 1.0022581, 1.001872,
+              1.0027552, 1.0019709, 1.0021086, 1.0030998, 1.0080754, 1.0013162,
+              1.0025258, 1.0044054, 1.0017514, 1.0026898, 1.0032914, 1.0032947,
+              1.0027568, 1.0056816, 1.0076947, 1.0022088, 1.0029486, 1.0037018,
+              1.0043061, 1.0032768, 1.0015746, 1.0046197, 1.0041125, 1.0060117,
+              1.0032651, 1.0015031, 1.0050989, 1.0046166, 1.0052769, 1.0035608,
+              1.0040259, 1.002186, 1.0046998, 1.0016583, 1.0048993, 1.0062689,
+              1e32, 1e32,
+            ],
+          ',arch=arm64,bench_type=skandroidcodec,compiler=Clang,config=nonrendering,cpu_or_gpu=CPU,cpu_or_gpu_value=Snapdragon855,extra_config=Android_Wuffs,model=Pixel4,name=AndroidCodec_01_original.jpg_SampleSize2,os=Android,source_type=image,sub_result=min_ms,test=AndroidCodec_01_original.jpg_SampleSize2_640_480,':
+            [
+              13.51672, 13.434168, 13.47146, 13.494012, 13.464116, 13.450209,
+              13.423439, 13.434011, 13.502189, 13.435418, 13.398959, 13.401876,
+              13.412658, 13.531095, 13.503022, 13.520366, 13.41073, 13.391043,
+              13.389377, 13.370262, 13.394116, 13.366512, 13.373126, 13.494376,
+              13.482189, 13.390887, 13.423231, 13.388387, 13.369949, 13.377084,
+              13.387605, 13.409533, 13.423283, 13.372189, 13.372918, 13.435366,
+              13.38495, 13.405939, 13.390105, 13.502606, 13.381928, 13.329532,
+              13.420783, 13.419793, 13.440002, 1e32, 13.488907, 1e32, 13.49323,
+              1e32,
+            ],
+          ',arch=arm64,bench_type=skandroidcodec,compiler=Clang,config=nonrendering,cpu_or_gpu=CPU,cpu_or_gpu_value=Snapdragon855,extra_config=Android_Wuffs,model=Pixel4,name=AndroidCodec_01_original.jpg_SampleSize2,os=Android,source_type=image,sub_result=min_ratio,test=AndroidCodec_01_original.jpg_SampleSize2_640_480,':
+            [
+              1.0060265, 1.0050516, 1.0032824, 1.0048633, 1.0030173, 1.0089877,
+              1.005564, 1.0143331, 1.0087292, 1.004551, 1.0055237, 1.0070225,
+              1.0050403, 1.0034873, 1.0055274, 1.0046265, 1.0032196, 1.0046984,
+              1.0029291, 1.0070547, 1.0091536, 1.007189, 1.0019591, 1.0073526,
+              1.0068724, 1.0070788, 1.0009079, 1.0057614, 1.0018076, 1.0048864,
+              1.0045946, 1.0053095, 1.0055135, 1.0083896, 1.007283, 1.009362,
+              1.0063659, 1.0073311, 1.0077171, 1.0068235, 1.0078387, 1.010589,
+              1.0133693, 1.001502, 1.0090255, 1e32, 1.0033516, 1e32, 1.0042962,
+              1e32,
+            ],
+        },
+        header: [
+          {
+            offset: 67125,
+            timestamp: 1687855198,
+          },
+          {
+            offset: 67126,
+            timestamp: 1687857789,
+          },
+          {
+            offset: 67127,
+            timestamp: 1687868015,
+          },
+          {
+            offset: 67128,
+            timestamp: 1687868368,
+          },
+          {
+            offset: 67129,
+            timestamp: 1687870256,
+          },
+          {
+            offset: 67130,
+            timestamp: 1687872763,
+          },
+          {
+            offset: 67131,
+            timestamp: 1687877748,
+          },
+          {
+            offset: 67132,
+            timestamp: 1687878083,
+          },
+          {
+            offset: 67133,
+            timestamp: 1687878588,
+          },
+          {
+            offset: 67134,
+            timestamp: 1687878658,
+          },
+          {
+            offset: 67135,
+            timestamp: 1687878976,
+          },
+          {
+            offset: 67136,
+            timestamp: 1687879230,
+          },
+          {
+            offset: 67137,
+            timestamp: 1687881375,
+          },
+          {
+            offset: 67138,
+            timestamp: 1687884748,
+          },
+          {
+            offset: 67139,
+            timestamp: 1687885047,
+          },
+          {
+            offset: 67140,
+            timestamp: 1687885507,
+          },
+          {
+            offset: 67141,
+            timestamp: 1687886132,
+          },
+          {
+            offset: 67142,
+            timestamp: 1687886787,
+          },
+          {
+            offset: 67143,
+            timestamp: 1687887013,
+          },
+          {
+            offset: 67144,
+            timestamp: 1687888513,
+          },
+          {
+            offset: 67145,
+            timestamp: 1687891891,
+          },
+          {
+            offset: 67146,
+            timestamp: 1687891925,
+          },
+          {
+            offset: 67147,
+            timestamp: 1687895229,
+          },
+          {
+            offset: 67148,
+            timestamp: 1687895693,
+          },
+          {
+            offset: 67149,
+            timestamp: 1687896092,
+          },
+          {
+            offset: 67150,
+            timestamp: 1687896114,
+          },
+          {
+            offset: 67151,
+            timestamp: 1687896459,
+          },
+          {
+            offset: 67152,
+            timestamp: 1687900291,
+          },
+          {
+            offset: 67153,
+            timestamp: 1687900389,
+          },
+          {
+            offset: 67154,
+            timestamp: 1687900992,
+          },
+          {
+            offset: 67155,
+            timestamp: 1687904682,
+          },
+          {
+            offset: 67156,
+            timestamp: 1687907669,
+          },
+          {
+            offset: 67157,
+            timestamp: 1687909158,
+          },
+          {
+            offset: 67158,
+            timestamp: 1687910749,
+          },
+          {
+            offset: 67159,
+            timestamp: 1687911636,
+          },
+          {
+            offset: 67160,
+            timestamp: 1687911698,
+          },
+          {
+            offset: 67161,
+            timestamp: 1687913983,
+          },
+          {
+            offset: 67162,
+            timestamp: 1687914369,
+          },
+          {
+            offset: 67163,
+            timestamp: 1687917173,
+          },
+          {
+            offset: 67164,
+            timestamp: 1687927827,
+          },
+          {
+            offset: 67165,
+            timestamp: 1687928532,
+          },
+          {
+            offset: 67166,
+            timestamp: 1687928754,
+          },
+          {
+            offset: 67167,
+            timestamp: 1687930648,
+          },
+          {
+            offset: 67168,
+            timestamp: 1687933565,
+          },
+          {
+            offset: 67169,
+            timestamp: 1687936673,
+          },
+          {
+            offset: 67170,
+            timestamp: 1687958245,
+          },
+          {
+            offset: 67171,
+            timestamp: 1687958371,
+          },
+          {
+            offset: 67172,
+            timestamp: 1687958912,
+          },
+          {
+            offset: 67173,
+            timestamp: 1687960354,
+          },
+          {
+            offset: 67174,
+            timestamp: 1687961972,
+          },
+        ],
+        paramset: {
+          arch: ['arm', 'arm64', 'x86_64'],
+          bench_type: ['skandroidcodec'],
+          compiler: ['Clang'],
+          config: ['nonrendering'],
+          configuration: ['OptimizeForSize'],
+          cpu_or_gpu: ['CPU'],
+          cpu_or_gpu_value: [
+            'AVX2',
+            'AVX512',
+            'Snapdragon855',
+            'SnapdragonQM215',
+          ],
+          extra_config: [
+            'Android',
+            'Android_Wuffs',
+            'ColorSpaces',
+            'Fast',
+            'SK_FORCE_RASTER_PIPELINE_BLITTER',
+            'Wuffs',
+          ],
+          model: ['GCE', 'JioNext', 'MacBookPro11.5', 'NUC9i7QN', 'Pixel4'],
+          name: ['AndroidCodec_01_original.jpg_SampleSize2'],
+          os: ['Android', 'Debian10', 'Debian11', 'Mac10.13', 'Win2019'],
+          source_type: ['image'],
+          sub_result: ['min_ms', 'min_ratio'],
+          test: ['AndroidCodec_01_original.jpg_SampleSize2_640_480'],
+        },
+        skip: 0,
+      },
+      skps: [],
+      msg: '',
+      display_mode: 'display_plot',
+      anomalymap: null,
+    },
+    url: '/_/status/d25fedcc-7e36-47e4-83d5-58ab76b2d3d1',
+  };
+
+  fetchMock.get(
+    '/_/status/d25fedcc-7e36-47e4-83d5-58ab76b2d3d1',
+    normalTracesResponse
+  );
+
+  fetchMock.post('/_/cid/', {
+    commitSlice: [
+      {
+        offset: 67193,
+        hash: '0d7087e5b99087f5945f04dbda7b7a7a4b12e344',
+        ts: 1687990261,
+        author: 'John Stiles (johnstiles@google.com)',
+        message: 'Remove Win10 + ANGLE + IrisXe test and perf jobs.',
+        url: 'https://skia.googlesource.com/skia/+show/0d7087e5b99087f5945f04dbda7b7a7a4b12e344',
+        body: '',
+      },
+      {
+        offset: 67194,
+        hash: '2894e7194406ad8014d3e85b39379ca0e4607ead',
+        ts: 1687991201,
+        author: 'Arman Uguray (armansito@google.com)',
+        message: 'Roll vello from ef2630ad to 12e764d5',
+        url: 'https://skia.googlesource.com/skia/+show/2894e7194406ad8014d3e85b39379ca0e4607ead',
+        body: '',
+      },
+    ],
+    logEntry:
+      'commit 0d7087e5b99087f5945f04dbda7b7a7a4b12e344\nAuthor John Stiles (johnstiles@google.com)\nDate 28 Jun 23 22:11 +0000\n\nRemove Win10 + ANGLE + IrisXe test and perf jobs.\n\nOnce skia:14417 is resolved, we should reinstate these jobs.\n\nBug: skia:14417\nChange-Id: Ib6b2a06cf7983c998d1d4e95a5e4973377b3bd48\nReviewed-on: https://skia-review.googlesource.com/c/skia/+/718157\nAuto-Submit: John Stiles \u003cjohnstiles@google.com\u003e\nCommit-Queue: Joe Gregorio \u003cjcgregorio@google.com\u003e\nCommit-Queue: John Stiles \u003cjohnstiles@google.com\u003e\nReviewed-by: Joe Gregorio \u003cjcgregorio@google.com\u003e\n',
+  });
+
+  fetchMock.post('/_/details/?results=false', {
+    gitHash: 'e539c1a62d339f6509463a7e59d83141576e3722',
+    key: {
+      arch: 'arm',
+      compiler: 'Clang',
+      cpu_or_gpu: 'CPU',
+      cpu_or_gpu_value: 'SnapdragonQM215',
+      extra_config: 'Android',
+      model: 'JioNext',
+      os: 'Android',
+    },
+    swarming_bot_id: 'skia-rpi2-rack1-shelf1-026',
+    swarming_task_id: '631c9c79d2c59211',
+  });
+
+  fetchMock.post('/_/shortcut/get', {
+    GraphConfig: {
+      queries: ['arch=arm64&bench_type=skandroidcodec'],
+    },
+  });
+
+  fetchMock.post('/_/nextParamList/', {
+    paramset: paramSet,
+    count: 4,
+  });
+
+  fetchMock.post('/_/shortcut/update', {
+    id: 'aaab78c9711cb79197d47f448ba51338',
+  });
+}
diff --git a/perf/modules/explore-multi-sk/BUILD.bazel b/perf/modules/explore-multi-sk/BUILD.bazel
index 2f26bf7..9a417fa 100644
--- a/perf/modules/explore-multi-sk/BUILD.bazel
+++ b/perf/modules/explore-multi-sk/BUILD.bazel
@@ -1,4 +1,4 @@
-load("//infra-sk:index.bzl", "sk_element")
+load("//infra-sk:index.bzl", "sk_demo_page_server", "sk_element", "sk_page")
 
 sk_element(
     name = "explore-multi-sk",
@@ -29,3 +29,20 @@
     ],
     visibility = ["//visibility:public"],
 )
+
+sk_demo_page_server(
+    name = "demo_page_server",
+    sk_page = ":explore-multi-sk-demo",
+)
+
+sk_page(
+    name = "explore-multi-sk-demo",
+    html_file = "explore-multi-sk-demo.html",
+    scss_entry_point = "explore-multi-sk-demo.scss",
+    sk_element_deps = [
+        "//elements-sk/modules/error-toast-sk",
+        ":explore-multi-sk",
+    ],
+    ts_deps = ["//perf/modules/common:test-util_ts_lib"],
+    ts_entry_point = "explore-multi-sk-demo.ts",
+)
diff --git a/perf/modules/explore-multi-sk/explore-multi-sk-demo.html b/perf/modules/explore-multi-sk/explore-multi-sk-demo.html
new file mode 100644
index 0000000..ea836a9
--- /dev/null
+++ b/perf/modules/explore-multi-sk/explore-multi-sk-demo.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>explore-sk</title>
+    <meta charset="utf-8" />
+    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+  </head>
+
+  <body class="body-sk font-sk">
+
+    <section class="body-sk font-sk darkmode">
+      <h1>explore-multi-sk</h1>
+      <!-- the element will appear here. -->
+    </section>
+    <error-toast-sk> </error-toast-sk>
+  </body>
+</html>
diff --git a/perf/modules/explore-multi-sk/explore-multi-sk-demo.scss b/perf/modules/explore-multi-sk/explore-multi-sk-demo.scss
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/perf/modules/explore-multi-sk/explore-multi-sk-demo.scss
diff --git a/perf/modules/explore-multi-sk/explore-multi-sk-demo.ts b/perf/modules/explore-multi-sk/explore-multi-sk-demo.ts
new file mode 100644
index 0000000..1d94c9a
--- /dev/null
+++ b/perf/modules/explore-multi-sk/explore-multi-sk-demo.ts
@@ -0,0 +1,34 @@
+import './index';
+import '../../../elements-sk/modules/error-toast-sk';
+import { setUpExploreDemoEnv } from '../common/test-util';
+
+setUpExploreDemoEnv();
+
+window.perf = {
+  commit_range_url: '',
+  key_order: ['config'],
+  demo: true,
+  radius: 7,
+  num_shift: 10,
+  interesting: 25,
+  step_up_only: false,
+  display_group_by: true,
+  hide_list_of_commits_on_explore: false,
+  notifications: 'none',
+  fetch_chrome_perf_anomalies: false,
+  feedback_url: '',
+  chat_url: '',
+  help_url_override: '',
+  trace_format: 'chrome',
+  need_alert_action: false,
+  bug_host_url: '',
+};
+
+customElements.whenDefined('explore-multi-sk').then(() => {
+  document
+    .querySelector('h1')!
+    .insertAdjacentElement(
+      'afterend',
+      document.createElement('explore-multi-sk')
+    );
+});
diff --git a/perf/modules/explore-sk/BUILD.bazel b/perf/modules/explore-sk/BUILD.bazel
index 55d84b8..e0eba98 100644
--- a/perf/modules/explore-sk/BUILD.bazel
+++ b/perf/modules/explore-sk/BUILD.bazel
@@ -45,6 +45,7 @@
         "//infra-sk/modules:dom_ts_lib",
         "//perf/modules/json:index_ts_lib",
         "//:node_modules/fetch-mock",
+        "//perf/modules/common:test-util_ts_lib",
     ],
     ts_entry_point = "explore-sk-demo.ts",
 )
diff --git a/perf/modules/explore-sk/explore-sk-demo.ts b/perf/modules/explore-sk/explore-sk-demo.ts
index 3039e95..f8379db 100644
--- a/perf/modules/explore-sk/explore-sk-demo.ts
+++ b/perf/modules/explore-sk/explore-sk-demo.ts
@@ -7,443 +7,9 @@
 import { ExploreSk } from './explore-sk';
 import { ExploreSimpleSk } from '../explore-simple-sk/explore-simple-sk';
 import { QueryConfig } from '../json';
+import { setUpExploreDemoEnv } from '../common/test-util';
 
-const status: Status = {
-  email: 'user@google.com',
-  roles: ['viewer', 'admin', 'editor', 'bisecter'],
-};
-
-fetchMock.get('/_/login/status', status);
-
-const paramSet = {
-  arch: ['arm', 'arm64', 'x86_64'],
-  bench_type: ['skandroidcodec'],
-  compiler: ['Clang'],
-  config: ['nonrendering'],
-  configuration: ['OptimizeForSize'],
-  cpu_or_gpu: ['CPU'],
-  cpu_or_gpu_value: ['AVX2', 'AVX512', 'Snapdragon855', 'SnapdragonQM215'],
-  extra_config: [
-    'Android',
-    'Android_Wuffs',
-    'ColorSpaces',
-    'Fast',
-    'SK_FORCE_RASTER_PIPELINE_BLITTER',
-    'Wuffs',
-  ],
-  model: ['GCE', 'JioNext', 'MacBookPro11.5', 'NUC9i7QN', 'Pixel4'],
-  name: ['AndroidCodec_01_original.jpg_SampleSize2'],
-  os: ['Android', 'Debian10', 'Debian11', 'Mac10.13', 'Win2019'],
-  source_type: ['image'],
-  sub_result: ['min_ms', 'min_ratio'],
-};
-
-fetchMock.get(/_\/initpage\/.*/, () => ({
-  dataframe: {
-    traceset: null,
-    header: null,
-    paramset: paramSet,
-    skip: 0,
-  },
-  ticks: [],
-  skps: [],
-  msg: '',
-}));
-
-fetchMock.post('/_/count/', {
-  count: 117, // Don't make the demo page non-deterministic.
-  paramset: paramSet,
-});
-
-fetchMock.post('/_/frame/start', {
-  status: 'Running',
-  messages: [],
-  url: '/_/status/d25fedcc-7e36-47e4-83d5-58ab76b2d3d1',
-});
-
-const defaultConfig: QueryConfig = {
-  default_param_selections: null,
-  default_url_values: null,
-  include_params: null,
-};
-
-fetchMock.get('/_/defaults/', defaultConfig);
-
-const normalTracesResponse = {
-  status: 'Finished',
-  messages: [
-    {
-      key: 'Step',
-      value: '1/1',
-    },
-  ],
-  results: {
-    dataframe: {
-      traceset: {
-        ',arch=arm,bench_type=skandroidcodec,compiler=Clang,config=nonrendering,cpu_or_gpu=CPU,cpu_or_gpu_value=SnapdragonQM215,extra_config=Android,model=JioNext,name=AndroidCodec_01_original.jpg_SampleSize2,os=Android,source_type=image,sub_result=min_ms,test=AndroidCodec_01_original.jpg_SampleSize2_640_480,':
-          [
-            61.2075, 60.687603, 61.30078, 61.660313, 60.830208, 60.854946,
-            60.8525, 61.43297, 61.24557, 61.098125, 61.284843, 60.7938,
-            61.741615, 62.60328, 60.93729, 60.925156, 63.232346, 61.770676,
-            62.252968, 61.87958, 61.140102, 62.40708, 62.869167, 60.893852,
-            61.042187, 61.17974, 61.73057, 61.754063, 60.726772, 61.837135,
-            61.868282, 61.161095, 61.88469, 60.81271, 61.4625, 60.91443,
-            60.806095, 60.81344, 61.624477, 60.98828, 60.838856, 61.989845,
-            60.84349, 61.973698, 61.97073, 60.615208, 62.083595, 61.148228,
-            1e32, 1e32,
-          ],
-        ',arch=arm,bench_type=skandroidcodec,compiler=Clang,config=nonrendering,cpu_or_gpu=CPU,cpu_or_gpu_value=SnapdragonQM215,extra_config=Android,model=JioNext,name=AndroidCodec_01_original.jpg_SampleSize2,os=Android,source_type=image,sub_result=min_ratio,test=AndroidCodec_01_original.jpg_SampleSize2_640_480,':
-          [
-            1.0053873, 1.0019164, 1.0029848, 1.002643, 1.0012664, 1.0028929,
-            1.0037411, 1.003003, 1.0050658, 1.007233, 1.0022581, 1.001872,
-            1.0027552, 1.0019709, 1.0021086, 1.0030998, 1.0080754, 1.0013162,
-            1.0025258, 1.0044054, 1.0017514, 1.0026898, 1.0032914, 1.0032947,
-            1.0027568, 1.0056816, 1.0076947, 1.0022088, 1.0029486, 1.0037018,
-            1.0043061, 1.0032768, 1.0015746, 1.0046197, 1.0041125, 1.0060117,
-            1.0032651, 1.0015031, 1.0050989, 1.0046166, 1.0052769, 1.0035608,
-            1.0040259, 1.002186, 1.0046998, 1.0016583, 1.0048993, 1.0062689,
-            1e32, 1e32,
-          ],
-        ',arch=arm64,bench_type=skandroidcodec,compiler=Clang,config=nonrendering,cpu_or_gpu=CPU,cpu_or_gpu_value=Snapdragon855,extra_config=Android_Wuffs,model=Pixel4,name=AndroidCodec_01_original.jpg_SampleSize2,os=Android,source_type=image,sub_result=min_ms,test=AndroidCodec_01_original.jpg_SampleSize2_640_480,':
-          [
-            13.51672, 13.434168, 13.47146, 13.494012, 13.464116, 13.450209,
-            13.423439, 13.434011, 13.502189, 13.435418, 13.398959, 13.401876,
-            13.412658, 13.531095, 13.503022, 13.520366, 13.41073, 13.391043,
-            13.389377, 13.370262, 13.394116, 13.366512, 13.373126, 13.494376,
-            13.482189, 13.390887, 13.423231, 13.388387, 13.369949, 13.377084,
-            13.387605, 13.409533, 13.423283, 13.372189, 13.372918, 13.435366,
-            13.38495, 13.405939, 13.390105, 13.502606, 13.381928, 13.329532,
-            13.420783, 13.419793, 13.440002, 1e32, 13.488907, 1e32, 13.49323,
-            1e32,
-          ],
-        ',arch=arm64,bench_type=skandroidcodec,compiler=Clang,config=nonrendering,cpu_or_gpu=CPU,cpu_or_gpu_value=Snapdragon855,extra_config=Android_Wuffs,model=Pixel4,name=AndroidCodec_01_original.jpg_SampleSize2,os=Android,source_type=image,sub_result=min_ratio,test=AndroidCodec_01_original.jpg_SampleSize2_640_480,':
-          [
-            1.0060265, 1.0050516, 1.0032824, 1.0048633, 1.0030173, 1.0089877,
-            1.005564, 1.0143331, 1.0087292, 1.004551, 1.0055237, 1.0070225,
-            1.0050403, 1.0034873, 1.0055274, 1.0046265, 1.0032196, 1.0046984,
-            1.0029291, 1.0070547, 1.0091536, 1.007189, 1.0019591, 1.0073526,
-            1.0068724, 1.0070788, 1.0009079, 1.0057614, 1.0018076, 1.0048864,
-            1.0045946, 1.0053095, 1.0055135, 1.0083896, 1.007283, 1.009362,
-            1.0063659, 1.0073311, 1.0077171, 1.0068235, 1.0078387, 1.010589,
-            1.0133693, 1.001502, 1.0090255, 1e32, 1.0033516, 1e32, 1.0042962,
-            1e32,
-          ],
-      },
-      header: [
-        {
-          offset: 67125,
-          timestamp: 1687855198,
-        },
-        {
-          offset: 67126,
-          timestamp: 1687857789,
-        },
-        {
-          offset: 67127,
-          timestamp: 1687868015,
-        },
-        {
-          offset: 67128,
-          timestamp: 1687868368,
-        },
-        {
-          offset: 67129,
-          timestamp: 1687870256,
-        },
-        {
-          offset: 67130,
-          timestamp: 1687872763,
-        },
-        {
-          offset: 67131,
-          timestamp: 1687877748,
-        },
-        {
-          offset: 67132,
-          timestamp: 1687878083,
-        },
-        {
-          offset: 67133,
-          timestamp: 1687878588,
-        },
-        {
-          offset: 67134,
-          timestamp: 1687878658,
-        },
-        {
-          offset: 67135,
-          timestamp: 1687878976,
-        },
-        {
-          offset: 67136,
-          timestamp: 1687879230,
-        },
-        {
-          offset: 67137,
-          timestamp: 1687881375,
-        },
-        {
-          offset: 67138,
-          timestamp: 1687884748,
-        },
-        {
-          offset: 67139,
-          timestamp: 1687885047,
-        },
-        {
-          offset: 67140,
-          timestamp: 1687885507,
-        },
-        {
-          offset: 67141,
-          timestamp: 1687886132,
-        },
-        {
-          offset: 67142,
-          timestamp: 1687886787,
-        },
-        {
-          offset: 67143,
-          timestamp: 1687887013,
-        },
-        {
-          offset: 67144,
-          timestamp: 1687888513,
-        },
-        {
-          offset: 67145,
-          timestamp: 1687891891,
-        },
-        {
-          offset: 67146,
-          timestamp: 1687891925,
-        },
-        {
-          offset: 67147,
-          timestamp: 1687895229,
-        },
-        {
-          offset: 67148,
-          timestamp: 1687895693,
-        },
-        {
-          offset: 67149,
-          timestamp: 1687896092,
-        },
-        {
-          offset: 67150,
-          timestamp: 1687896114,
-        },
-        {
-          offset: 67151,
-          timestamp: 1687896459,
-        },
-        {
-          offset: 67152,
-          timestamp: 1687900291,
-        },
-        {
-          offset: 67153,
-          timestamp: 1687900389,
-        },
-        {
-          offset: 67154,
-          timestamp: 1687900992,
-        },
-        {
-          offset: 67155,
-          timestamp: 1687904682,
-        },
-        {
-          offset: 67156,
-          timestamp: 1687907669,
-        },
-        {
-          offset: 67157,
-          timestamp: 1687909158,
-        },
-        {
-          offset: 67158,
-          timestamp: 1687910749,
-        },
-        {
-          offset: 67159,
-          timestamp: 1687911636,
-        },
-        {
-          offset: 67160,
-          timestamp: 1687911698,
-        },
-        {
-          offset: 67161,
-          timestamp: 1687913983,
-        },
-        {
-          offset: 67162,
-          timestamp: 1687914369,
-        },
-        {
-          offset: 67163,
-          timestamp: 1687917173,
-        },
-        {
-          offset: 67164,
-          timestamp: 1687927827,
-        },
-        {
-          offset: 67165,
-          timestamp: 1687928532,
-        },
-        {
-          offset: 67166,
-          timestamp: 1687928754,
-        },
-        {
-          offset: 67167,
-          timestamp: 1687930648,
-        },
-        {
-          offset: 67168,
-          timestamp: 1687933565,
-        },
-        {
-          offset: 67169,
-          timestamp: 1687936673,
-        },
-        {
-          offset: 67170,
-          timestamp: 1687958245,
-        },
-        {
-          offset: 67171,
-          timestamp: 1687958371,
-        },
-        {
-          offset: 67172,
-          timestamp: 1687958912,
-        },
-        {
-          offset: 67173,
-          timestamp: 1687960354,
-        },
-        {
-          offset: 67174,
-          timestamp: 1687961972,
-        },
-      ],
-      paramset: {
-        arch: ['arm', 'arm64', 'x86_64'],
-        bench_type: ['skandroidcodec'],
-        compiler: ['Clang'],
-        config: ['nonrendering'],
-        configuration: ['OptimizeForSize'],
-        cpu_or_gpu: ['CPU'],
-        cpu_or_gpu_value: [
-          'AVX2',
-          'AVX512',
-          'Snapdragon855',
-          'SnapdragonQM215',
-        ],
-        extra_config: [
-          'Android',
-          'Android_Wuffs',
-          'ColorSpaces',
-          'Fast',
-          'SK_FORCE_RASTER_PIPELINE_BLITTER',
-          'Wuffs',
-        ],
-        model: ['GCE', 'JioNext', 'MacBookPro11.5', 'NUC9i7QN', 'Pixel4'],
-        name: ['AndroidCodec_01_original.jpg_SampleSize2'],
-        os: ['Android', 'Debian10', 'Debian11', 'Mac10.13', 'Win2019'],
-        source_type: ['image'],
-        sub_result: ['min_ms', 'min_ratio'],
-        test: ['AndroidCodec_01_original.jpg_SampleSize2_640_480'],
-      },
-      skip: 0,
-    },
-    skps: [],
-    msg: '',
-    display_mode: 'display_plot',
-    anomalymap: null,
-  },
-  url: '/_/status/d25fedcc-7e36-47e4-83d5-58ab76b2d3d1',
-};
-
-const calculationTraceSet = {
-  'avg(",arch=arm,bench_type=skandroidcodec,compiler=Clang,")': [
-    61.2075, 60.687603, 61.30078, 61.660313, 60.830208, 60.854946, 60.8525,
-    61.43297, 61.24557, 61.098125, 61.284843, 60.7938, 61.741615, 62.60328,
-    60.93729, 60.925156, 63.232346, 61.770676, 62.252968, 61.87958, 61.140102,
-    62.40708, 62.869167, 60.893852, 61.042187, 61.17974, 61.73057, 61.754063,
-    60.726772, 61.837135, 61.868282, 61.161095, 61.88469, 60.81271, 61.4625,
-    60.91443, 60.806095, 60.81344, 61.624477, 60.98828, 60.838856, 61.989845,
-    60.84349, 61.973698, 61.97073, 60.615208, 62.083595, 61.148228, 1e32, 1e32,
-  ],
-  'avg(",arch=arm64,bench_type=skandroidcodec,compiler=Clang,config=nonrendering,cpu_or_gpu=CPU,")':
-    [
-      1.0060265, 1.0050516, 1.0032824, 1.0048633, 1.0030173, 1.0089877,
-      1.005564, 1.0143331, 1.0087292, 1.004551, 1.0055237, 1.0070225, 1.0050403,
-      1.0034873, 1.0055274, 1.0046265, 1.0032196, 1.0046984, 1.0029291,
-      1.0070547, 1.0091536, 1.007189, 1.0019591, 1.0073526, 1.0068724,
-      1.0070788, 1.0009079, 1.0057614, 1.0018076, 1.0048864, 1.0045946,
-      1.0053095, 1.0055135, 1.0083896, 1.007283, 1.009362, 1.0063659, 1.0073311,
-      1.0077171, 1.0068235, 1.0078387, 1.010589, 1.0133693, 1.001502, 1.0090255,
-      1e32, 1.0033516, 1e32, 1.0042962, 1e32,
-    ],
-};
-
-let returnCalculationsInResponse = false;
-
-const calculationResponse = JSON.parse(JSON.stringify(normalTracesResponse));
-
-calculationResponse.results.dataframe.traceset = calculationTraceSet;
-
-fetchMock.get('/_/status/d25fedcc-7e36-47e4-83d5-58ab76b2d3d1', () => {
-  if (returnCalculationsInResponse) {
-    return calculationResponse;
-  }
-  return normalTracesResponse;
-});
-
-fetchMock.post('/_/cid/', {
-  commitSlice: [
-    {
-      offset: 67193,
-      hash: '0d7087e5b99087f5945f04dbda7b7a7a4b12e344',
-      ts: 1687990261,
-      author: 'John Stiles (johnstiles@google.com)',
-      message: 'Remove Win10 + ANGLE + IrisXe test and perf jobs.',
-      url: 'https://skia.googlesource.com/skia/+show/0d7087e5b99087f5945f04dbda7b7a7a4b12e344',
-      body: '',
-    },
-    {
-      offset: 67194,
-      hash: '2894e7194406ad8014d3e85b39379ca0e4607ead',
-      ts: 1687991201,
-      author: 'Arman Uguray (armansito@google.com)',
-      message: 'Roll vello from ef2630ad to 12e764d5',
-      url: 'https://skia.googlesource.com/skia/+show/2894e7194406ad8014d3e85b39379ca0e4607ead',
-      body: '',
-    },
-  ],
-  logEntry:
-    'commit 0d7087e5b99087f5945f04dbda7b7a7a4b12e344\nAuthor John Stiles (johnstiles@google.com)\nDate 28 Jun 23 22:11 +0000\n\nRemove Win10 + ANGLE + IrisXe test and perf jobs.\n\nOnce skia:14417 is resolved, we should reinstate these jobs.\n\nBug: skia:14417\nChange-Id: Ib6b2a06cf7983c998d1d4e95a5e4973377b3bd48\nReviewed-on: https://skia-review.googlesource.com/c/skia/+/718157\nAuto-Submit: John Stiles \u003cjohnstiles@google.com\u003e\nCommit-Queue: Joe Gregorio \u003cjcgregorio@google.com\u003e\nCommit-Queue: John Stiles \u003cjohnstiles@google.com\u003e\nReviewed-by: Joe Gregorio \u003cjcgregorio@google.com\u003e\n',
-});
-
-fetchMock.post('/_/details/?results=false', {
-  gitHash: 'e539c1a62d339f6509463a7e59d83141576e3722',
-  key: {
-    arch: 'arm',
-    compiler: 'Clang',
-    cpu_or_gpu: 'CPU',
-    cpu_or_gpu_value: 'SnapdragonQM215',
-    extra_config: 'Android',
-    model: 'JioNext',
-    os: 'Android',
-  },
-  swarming_bot_id: 'skia-rpi2-rack1-shelf1-026',
-  swarming_task_id: '631c9c79d2c59211',
-});
+setUpExploreDemoEnv();
 
 window.perf = {
   commit_range_url: '',
@@ -510,7 +76,6 @@
   });
 
   $$('#demo-load-traces')?.addEventListener('click', () => {
-    returnCalculationsInResponse = false;
     window.perf.fetch_chrome_perf_anomalies = false;
     // eslint-disable-next-line dot-notation
     explore!['query']!.current_query = 'arch=arm';
@@ -518,7 +83,6 @@
   });
 
   $$('#demo-show-bisect-button')?.addEventListener('click', () => {
-    returnCalculationsInResponse = false;
     window.perf.fetch_chrome_perf_anomalies = true;
     // eslint-disable-next-line dot-notation
     explore!['query']!.current_query = 'arch=arm';
@@ -526,7 +90,6 @@
   });
 
   $$('#demo-select-trace')?.addEventListener('click', () => {
-    returnCalculationsInResponse = false;
     window.perf.fetch_chrome_perf_anomalies = true;
 
     // First load the data.
@@ -539,8 +102,6 @@
   });
 
   $$('#demo-select-calc-trace')?.addEventListener('click', () => {
-    returnCalculationsInResponse = true;
-
     // First load data.
     explore!['query']!.current_query = 'arch=arm';
     explore!.add(true, 'query');
diff --git a/perf/modules/picker-field-sk/picker-field-sk-demo.html b/perf/modules/picker-field-sk/picker-field-sk-demo.html
index 41514b3..da143f1 100644
--- a/perf/modules/picker-field-sk/picker-field-sk-demo.html
+++ b/perf/modules/picker-field-sk/picker-field-sk-demo.html
@@ -21,5 +21,7 @@
   <button id="demo-focus2">Focus 2nd box</button>
   <button id="demo-fill">Autofill 2nd box</button>
   <button id="demo-open">Open overlay of 2nd box</button>
+  <button id="demo-disable">Disable 2nd box</button>
+  <button id="demo-enable">Enable 2nd box</button>
 </body>
 </html>
diff --git a/perf/modules/picker-field-sk/picker-field-sk-demo.ts b/perf/modules/picker-field-sk/picker-field-sk-demo.ts
index 17b4d00..e0edc9c 100644
--- a/perf/modules/picker-field-sk/picker-field-sk-demo.ts
+++ b/perf/modules/picker-field-sk/picker-field-sk-demo.ts
@@ -31,3 +31,13 @@
   const ele = document.querySelector('#focus-and-fill') as PickerFieldSk;
   ele.openOverlay();
 });
+
+$$('#demo-disable')?.addEventListener('click', () => {
+  const ele = document.querySelector('#focus-and-fill') as PickerFieldSk;
+  ele.disable();
+});
+
+$$('#demo-enable')?.addEventListener('click', () => {
+  const ele = document.querySelector('#focus-and-fill') as PickerFieldSk;
+  ele.enable();
+});
diff --git a/perf/modules/picker-field-sk/picker-field-sk.ts b/perf/modules/picker-field-sk/picker-field-sk.ts
index 0fe5333..b0778a4 100644
--- a/perf/modules/picker-field-sk/picker-field-sk.ts
+++ b/perf/modules/picker-field-sk/picker-field-sk.ts
@@ -69,6 +69,16 @@
     this._render();
   }
 
+  disable() {
+    this._comboBox!.setAttribute('readonly', '');
+    this._render();
+  }
+
+  enable() {
+    this._comboBox!.removeAttribute('readonly');
+    this._render();
+  }
+
   clear() {
     this.setValue('');
   }
diff --git a/perf/modules/test-picker-sk/test-picker-sk.ts b/perf/modules/test-picker-sk/test-picker-sk.ts
index b7d733d..95f0942 100644
--- a/perf/modules/test-picker-sk/test-picker-sk.ts
+++ b/perf/modules/test-picker-sk/test-picker-sk.ts
@@ -208,6 +208,21 @@
   }
 
   /**
+   * Sets the readonly property for all rendered fields.
+   *
+   * @param readonly
+   */
+  private setReadOnly(readonly: boolean) {
+    this._fieldData.forEach((field) => {
+      if (readonly) {
+        field.field?.disable();
+      } else {
+        field.field?.enable();
+      }
+    });
+  }
+
+  /**
    * Wrapper for POST Call to backend.
    *
    * @param handler
@@ -217,6 +232,7 @@
   ) {
     this.updateCount(-1);
     this._requestInProgress = true;
+    this.setReadOnly(true);
     this._render();
 
     const body: NextParamListHandlerRequest = {
@@ -233,10 +249,12 @@
       .then(jsonOrThrow)
       .then((json) => {
         this._requestInProgress = false;
+        this.setReadOnly(false);
         handler(json);
       })
       .catch((msg: any) => {
         this._requestInProgress = false;
+        this.setReadOnly(false);
         this._render();
         errorMessage(msg);
       });