This is an automated email from the ASF dual-hosted git repository.

yao pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/spark.git


The following commit(s) were added to refs/heads/master by this push:
     new 81f21722875d [SPARK-55985][WEBUI] Remove `jquery.blockUI.min.js`
81f21722875d is described below

commit 81f21722875db1d593578dc5ce6355682d667688
Author: Kousuke Saruta <[email protected]>
AuthorDate: Sat Mar 14 20:52:48 2026 +0800

    [SPARK-55985][WEBUI] Remove `jquery.blockUI.min.js`
    
    ### What changes were proposed in this pull request?
    This PR aims to remove `jquery.blockUI.min.js` and replace the jQuery 
BlockUI overlay with a Bootstrap 5 Spinner + CSS overlay.
    This change includes:
    - **Removed** `jquery.blockUI.min.js` and its entry in `dev/.rat-excludes`
    - **Removed** the BlockUI `<script>` tag from `UIUtils.scala`
    - **Added** a `<div id="loading-overlay">` with a Bootstrap 5 Spinner to 
the `<body>` in `UIUtils.scala` (initially hidden via `d-none`)
    - **Updated** `executorspage.js`, `historypage.js`, and `stagepage.js` to 
toggle the overlay visibility using 
`$("#loading-overlay").removeClass("d-none")` / `.addClass("d-none")` instead 
of `$.blockUI()` / `$.unblockUI()`
    - **Added** `#loading-overlay` styles to `webui.css` with dark mode support
    - **Added** `ui-test/tests/loading-overlay.test.js` with tests for DOM 
structure, show/hide behavior, and dark mode CSS
    
    Instead of relying on the jQuery BlockUI plugin to dynamically generate and 
remove overlay elements, the new implementation:
    
    1. Embeds a static overlay `<div>` with a Bootstrap 5 Spinner in the page 
HTML (hidden by default with `d-none`)
    2. Shows/hides the overlay by toggling the `d-none` class via jQuery's 
`addClass`/`removeClass`
    3. Uses CSS (`position: fixed`, `z-index: 1050`, semi-transparent 
background) for positioning and appearance
    4. Supports dark mode via `[data-bs-theme="dark"] #loading-overlay` selector
    
    Overlay will be changed like as follows.
    
    * Before (light mode)
    <img width="1200" height="863" alt="spark-ui-overlay-light-before" 
src="https://github.com/user-attachments/assets/fb7934e4-a2a9-4128-99f4-187484b57459";
 />
    
    * After (light mode)
    <img width="1200" height="863" alt="spark-ui-overlay-light" 
src="https://github.com/user-attachments/assets/ba1f53e9-9132-4d50-8715-242f2f6d5fd9";
 />
    
    * Before (dark mode)
    <img width="1200" height="863" alt="spark-ui-overlay-dark-before" 
src="https://github.com/user-attachments/assets/bef5b69a-e015-409a-9257-1bf015ab4a57";
 />
    
    * After (dark mode)
    <img width="1200" height="863" alt="spark-ui-overlay-dark" 
src="https://github.com/user-attachments/assets/504078ad-3eef-4394-b574-abfd365132be";
 />
    
    ### Why are the changes needed?
    `jquery.blockUI.min.js` is a legacy jQuery plugin that is no longer 
necessary. The same overlay functionality can be achieved with Bootstrap 5 
utilities and plain CSS, which are already available in the Spark Web UI. 
Removing this dependency reduces the JavaScript footprint and aligns with the 
Web UI modernization goals.
    
    ### Does this PR introduce _any_ user-facing change?
    Yes but it's just a slight change in appearance.
    
    ### How was this patch tested?
    - New tests: `ui-test/tests/loading-overlay.test.js` (3 tests — DOM 
structure, show/hide behavior, dark mode CSS)
    - Manual verification with screenshots in both light and dark modes (before 
and after)
    
    ### Was this patch authored or co-authored using generative AI tooling?
    Kiro CLI / Opus 4.6
    
    Closes #54784 from sarutak/remove-block-ui.
    
    Authored-by: Kousuke Saruta <[email protected]>
    Signed-off-by: Kent Yao <[email protected]>
---
 .../org/apache/spark/ui/static/executorspage.js    |  6 +-
 .../org/apache/spark/ui/static/historypage.js      |  8 ++-
 .../apache/spark/ui/static/jquery.blockUI.min.js   |  6 --
 .../org/apache/spark/ui/static/stagepage.js        |  4 +-
 .../resources/org/apache/spark/ui/static/webui.css |  9 +++
 .../main/scala/org/apache/spark/ui/UIUtils.scala   | 21 +++++-
 dev/.rat-excludes                                  |  1 -
 ui-test/tests/loading-overlay.test.js              | 83 ++++++++++++++++++++++
 8 files changed, 123 insertions(+), 15 deletions(-)

diff --git 
a/core/src/main/resources/org/apache/spark/ui/static/executorspage.js 
b/core/src/main/resources/org/apache/spark/ui/static/executorspage.js
index a10d7fe2a703..b1eae834c73a 100644
--- a/core/src/main/resources/org/apache/spark/ui/static/executorspage.js
+++ b/core/src/main/resources/org/apache/spark/ui/static/executorspage.js
@@ -269,9 +269,11 @@ $.extend($.fn.dataTableExt.oSort, {
   }
 });
 
-$(document).ajaxStop($.unblockUI);
+$(document).ajaxStop(function () {
+  $("#loading-overlay").addClass("d-none");
+});
 $(document).ajaxStart(function () {
-  $.blockUI({message: '<h3>Loading Executors Page...</h3>'});
+  $("#loading-overlay").removeClass("d-none");
 });
 
 function logsExist(execs) {
diff --git a/core/src/main/resources/org/apache/spark/ui/static/historypage.js 
b/core/src/main/resources/org/apache/spark/ui/static/historypage.js
index f5dd5a0ab763..764b3e97b1e6 100644
--- a/core/src/main/resources/org/apache/spark/ui/static/historypage.js
+++ b/core/src/main/resources/org/apache/spark/ui/static/historypage.js
@@ -109,9 +109,11 @@ jQuery.extend( jQuery.fn.dataTableExt.ofnSearch, {
   }
 });
 
-$(document).ajaxStop($.unblockUI);
-$(document).ajaxStart(function(){
-  $.blockUI({ message: '<h3>Loading history summary...</h3>'});
+$(document).ajaxStop(function () {
+  $("#loading-overlay").addClass("d-none");
+});
+$(document).ajaxStart(function () {
+  $("#loading-overlay").removeClass("d-none");
 });
 
 $(document).ready(function() {
diff --git 
a/core/src/main/resources/org/apache/spark/ui/static/jquery.blockUI.min.js 
b/core/src/main/resources/org/apache/spark/ui/static/jquery.blockUI.min.js
deleted file mode 100644
index 6f5256064bd2..000000000000
--- a/core/src/main/resources/org/apache/spark/ui/static/jquery.blockUI.min.js
+++ /dev/null
@@ -1,6 +0,0 @@
-/*
-* jQuery BlockUI; v20141123
-* http://jquery.malsup.com/block/
-* Copyright (c) 2014 M. Alsup; Dual licensed: MIT/GPL
-*/
-(function(){"use strict";function e(e){function o(o,i){var 
s,h,k=o==window,v=i&&void 0!==i.message?i.message:void 
0;if(i=e.extend({},e.blockUI.defaults,i||{}),!i.ignoreIfBlocked||!e(o).data("blockUI.isBlocked")){if(i.overlayCSS=e.extend({},e.blockUI.defaults.overlayCSS,i.overlayCSS||{}),s=e.extend({},e.blockUI.defaults.css,i.css||{}),i.onOverlayClick&&(i.overlayCSS.cursor="pointer"),h=e.extend({},e.blockUI.defaults.themedCSS,i.themedCSS||{}),v=void
 0===v?i.message:v,k&&b&&t(window,{fadeO [...]
\ No newline at end of file
diff --git a/core/src/main/resources/org/apache/spark/ui/static/stagepage.js 
b/core/src/main/resources/org/apache/spark/ui/static/stagepage.js
index dcba4f544b50..f41a5d49f477 100644
--- a/core/src/main/resources/org/apache/spark/ui/static/stagepage.js
+++ b/core/src/main/resources/org/apache/spark/ui/static/stagepage.js
@@ -42,14 +42,14 @@ function setTaskThreadDumpEnabled(enabled){
 
 $(document).ajaxStop(function () {
   if (shouldBlockUI) {
-    $.unblockUI();
+    $("#loading-overlay").addClass("d-none");
     shouldBlockUI = false;
   }
 });
 
 $(document).ajaxStart(function () {
   if (shouldBlockUI) {
-    $.blockUI({message: '<h3>Loading Stage Page...</h3>'});
+    $("#loading-overlay").removeClass("d-none");
   }
 });
 
diff --git a/core/src/main/resources/org/apache/spark/ui/static/webui.css 
b/core/src/main/resources/org/apache/spark/ui/static/webui.css
index 69ce54532274..c407c8cb22bd 100755
--- a/core/src/main/resources/org/apache/spark/ui/static/webui.css
+++ b/core/src/main/resources/org/apache/spark/ui/static/webui.css
@@ -514,3 +514,12 @@ a.downloadbutton {
 .tab-content {
   margin-top: 10px;
 }
+
+#loading-overlay {
+  z-index: 1050;
+  background-color: rgba(255, 255, 255, 0.75);
+}
+
+[data-bs-theme="dark"] #loading-overlay {
+  background-color: rgba(0, 0, 0, 0.75);
+}
diff --git a/core/src/main/scala/org/apache/spark/ui/UIUtils.scala 
b/core/src/main/scala/org/apache/spark/ui/UIUtils.scala
index 51b96dd27497..7434cf131497 100644
--- a/core/src/main/scala/org/apache/spark/ui/UIUtils.scala
+++ b/core/src/main/scala/org/apache/spark/ui/UIUtils.scala
@@ -256,7 +256,6 @@ private[spark] object UIUtils extends Logging {
           href={prependBaseUri(request, "/static/webui-dataTables.css")} 
type="text/css"/>
     <script src={prependBaseUri(request, 
"/static/jquery.dataTables.min.js")}></script>
     <script src={prependBaseUri(request, 
"/static/jquery.cookies.2.2.0.min.js")}></script>
-    <script src={prependBaseUri(request, 
"/static/jquery.blockUI.min.js")}></script>
     <script src={prependBaseUri(request, 
"/static/dataTables.bootstrap5.min.js")}></script>
   }
 
@@ -295,6 +294,16 @@ private[spark] object UIUtils extends Logging {
         <title>{appName} - {title}</title>
       </head>
       <body class="d-flex flex-column min-vh-100">
+        <div id="loading-overlay"
+             class={"position-fixed top-0 start-0 w-100 h-100" +
+               " d-flex justify-content-center align-items-center d-none"}>
+          <div class="text-center">
+            <div class="spinner-border text-primary" role="status">
+              <span class="visually-hidden">Loading...</span>
+            </div>
+            <h3 class="mt-2">Loading...</h3>
+          </div>
+        </div>
         <nav class="navbar navbar-expand-md navbar-light bg-light mb-4">
           <div class="navbar-header">
             <div class="navbar-brand">
@@ -358,6 +367,16 @@ private[spark] object UIUtils extends Logging {
         <title>{title}</title>
       </head>
       <body class="d-flex flex-column min-vh-100">
+        <div id="loading-overlay"
+             class={"position-fixed top-0 start-0 w-100 h-100" +
+               " d-flex justify-content-center align-items-center d-none"}>
+          <div class="text-center">
+            <div class="spinner-border text-primary" role="status">
+              <span class="visually-hidden">Loading...</span>
+            </div>
+            <h3 class="mt-2">Loading...</h3>
+          </div>
+        </div>
         <div class="container-fluid flex-fill">
           <div class="row">
             <div class="col-12">
diff --git a/dev/.rat-excludes b/dev/.rat-excludes
index 224e09003752..390d27b2ecba 100644
--- a/dev/.rat-excludes
+++ b/dev/.rat-excludes
@@ -35,7 +35,6 @@ vis-timeline-graph2d.min.css
 dataTables.bootstrap5.min.css
 dataTables.bootstrap5.min.js
 dataTables.rowsGroup.js
-jquery.blockUI.min.js
 jquery.cookies.2.2.0.min.js
 jquery.dataTables.min.css
 jquery.dataTables.min.js
diff --git a/ui-test/tests/loading-overlay.test.js 
b/ui-test/tests/loading-overlay.test.js
new file mode 100644
index 000000000000..9d4e0bf24a7f
--- /dev/null
+++ b/ui-test/tests/loading-overlay.test.js
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.
+ */
+
+import { readFileSync } from 'fs';
+import { join, dirname } from 'path';
+import { fileURLToPath } from 'url';
+
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = dirname(__filename);
+const staticDir = join(__dirname, 
'../../core/src/main/resources/org/apache/spark/ui/static');
+
+function readStatic(filename) {
+  return readFileSync(join(staticDir, filename), 'utf-8');
+}
+
+describe("loading overlay", () => {
+  test("overlay has expected DOM structure", () => {
+    document.body.innerHTML = `
+      <div id="loading-overlay" class="position-fixed top-0 start-0 w-100 
h-100 d-flex justify-content-center align-items-center d-none">
+        <div class="text-center">
+          <div class="spinner-border text-primary" role="status">
+            <span class="visually-hidden">Loading...</span>
+          </div>
+          <h3 class="mt-2">Loading...</h3>
+        </div>
+      </div>`;
+
+    const overlay = document.getElementById("loading-overlay");
+    expect(overlay).not.toBeNull();
+    expect(overlay.classList.contains("d-none")).toBe(true);
+    expect(overlay.classList.contains("position-fixed")).toBe(true);
+
+    const spinner = overlay.querySelector(".spinner-border");
+    expect(spinner).not.toBeNull();
+    expect(spinner.getAttribute("role")).toBe("status");
+    
expect(spinner.querySelector(".visually-hidden").textContent).toBe("Loading...");
+  });
+
+  test("overlay can be shown and hidden via d-none class", () => {
+    document.body.innerHTML = `
+      <div id="loading-overlay" class="position-fixed top-0 start-0 w-100 
h-100 d-flex justify-content-center align-items-center d-none">
+        <div class="text-center">
+          <div class="spinner-border text-primary" role="status">
+            <span class="visually-hidden">Loading...</span>
+          </div>
+          <h3 class="mt-2">Loading...</h3>
+        </div>
+      </div>`;
+
+    const overlay = document.getElementById("loading-overlay");
+
+    // Initially hidden
+    expect(overlay.classList.contains("d-none")).toBe(true);
+
+    // Show overlay
+    overlay.classList.remove("d-none");
+    expect(overlay.classList.contains("d-none")).toBe(false);
+
+    // Hide overlay
+    overlay.classList.add("d-none");
+    expect(overlay.classList.contains("d-none")).toBe(true);
+  });
+
+  test("CSS has dark mode support for overlay", () => {
+    const css = readStatic("webui.css");
+    expect(css).toContain("#loading-overlay");
+    expect(css).toContain('[data-bs-theme="dark"] #loading-overlay');
+  });
+});


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to