This is an automated email from the ASF dual-hosted git repository.
pierrejeambrun pushed a commit to branch v2-11-test
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/v2-11-test by this push:
new ff317535f20 Fix log groups collapsing on auto-refresh in Logs view
(#65378)
ff317535f20 is described below
commit ff317535f20d548ce40fde4c36dc86bd3e36cdf7
Author: Pierre Jeambrun <[email protected]>
AuthorDate: Thu Apr 16 23:36:54 2026 +0200
Fix log groups collapsing on auto-refresh in Logs view (#65378)
Log group sections (::group::) were auto-collapsing whenever the UI
auto-refreshed or the user scrolled. Two issues combined to cause this:
1. LogBlock.onClick mutated the unfoldedGroups array in-place (.push,
.splice) then passed the same reference to setState. React uses
Object.is for state comparison — same reference means the update is
silently discarded. This worked by accident in React 18 but broke
after the React 19 upgrade (7d4c79b392).
2. Chakra UI Tabs with isLazy (default lazyBehavior="unmount") unmounts
inactive panels, resetting all component state on remount.
Fix: use immutable state updates (spread/filter to create new arrays)
so React properly detects changes, and add lazyBehavior="keepMounted"
so tab panels preserve their state once rendered.
closes: #65200
---
airflow/www/static/js/dag/details/index.tsx | 1 +
.../js/dag/details/taskInstance/Logs/LogBlock.tsx | 18 ++++++------------
.../static/js/dag/details/taskInstance/Logs/index.tsx | 1 -
3 files changed, 7 insertions(+), 13 deletions(-)
diff --git a/airflow/www/static/js/dag/details/index.tsx
b/airflow/www/static/js/dag/details/index.tsx
index aa3e42ab3f3..f4cd3c502e6 100644
--- a/airflow/www/static/js/dag/details/index.tsx
+++ b/airflow/www/static/js/dag/details/index.tsx
@@ -309,6 +309,7 @@ const Details = ({
<Tabs
size="lg"
isLazy
+ lazyBehavior="keepMounted"
height="100%"
index={tabIndex}
onChange={onChangeTab}
diff --git a/airflow/www/static/js/dag/details/taskInstance/Logs/LogBlock.tsx
b/airflow/www/static/js/dag/details/taskInstance/Logs/LogBlock.tsx
index 9072027e75d..e714c9c1eff 100644
--- a/airflow/www/static/js/dag/details/taskInstance/Logs/LogBlock.tsx
+++ b/airflow/www/static/js/dag/details/taskInstance/Logs/LogBlock.tsx
@@ -25,7 +25,6 @@ interface Props {
parsedLogs: string;
wrap: boolean;
tryNumber: number;
- unfoldedGroups: Array<string>;
setUnfoldedLogGroup: React.Dispatch<React.SetStateAction<string[]>>;
}
@@ -33,7 +32,6 @@ const LogBlock = ({
parsedLogs,
wrap,
tryNumber,
- unfoldedGroups,
setUnfoldedLogGroup,
}: Props) => {
const [autoScroll, setAutoScroll] = useState(true);
@@ -74,10 +72,11 @@ const LogBlock = ({
0,
target.id.length - unfoldIdSuffix.length,
);
- // remember the folding state if logs re-loaded
- unfoldedGroups.push(gId);
- setUnfoldedLogGroup(unfoldedGroups);
- // now do the folding
+ // Persist the unfolded state so it survives re-renders (auto-refresh).
+ // A new array is required for React to detect the state change.
+ setUnfoldedLogGroup((prev) => [...prev, gId]);
+ // Immediate DOM update so the user sees the change without waiting
+ // for the next render cycle.
target.style.display = "none";
if (target.nextElementSibling) {
(target.nextElementSibling as HTMLElement).style.display = "inline";
@@ -87,12 +86,7 @@ const LogBlock = ({
0,
target.id.length - foldIdSuffix.length,
);
- // remember the folding state if logs re-loaded
- if (unfoldedGroups.indexOf(gId) >= 0) {
- unfoldedGroups.splice(unfoldedGroups.indexOf(gId), 1);
- }
- setUnfoldedLogGroup(unfoldedGroups);
- // now do the folding
+ setUnfoldedLogGroup((prev) => prev.filter((id) => id !== gId));
if (target.parentElement) {
target.parentElement.style.display = "none";
if (target.parentElement.previousSibling) {
diff --git a/airflow/www/static/js/dag/details/taskInstance/Logs/index.tsx
b/airflow/www/static/js/dag/details/taskInstance/Logs/index.tsx
index 40a3e26e198..0e5d9bbfbc4 100644
--- a/airflow/www/static/js/dag/details/taskInstance/Logs/index.tsx
+++ b/airflow/www/static/js/dag/details/taskInstance/Logs/index.tsx
@@ -278,7 +278,6 @@ const Logs = ({
parsedLogs={parsedLogs}
wrap={wrap}
tryNumber={selectedTryNumber}
- unfoldedGroups={unfoldedLogGroups}
setUnfoldedLogGroup={setUnfoldedLogGroup}
/>
)