This is an automated email from the ASF dual-hosted git repository.
bbovenzi pushed a commit to branch v3-4-test
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/v3-4-test by this push:
new 2587ff2ccd9 [v3-4-test] Make asset state store entries scrollable
(#75870) (#76079)
2587ff2ccd9 is described below
commit 2587ff2ccd9a954376d0a9b2a26b6a6f8e1d08ce
Author: github-actions[bot]
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Fri Oct 30 19:10:57 2026 +0000
[v3-4-test] Make asset state store entries scrollable (#75870) (#76079)
With many state-store entries, the table can extend beyond the visible
asset details panel, leaving users unable to reach older entries.
(cherry picked from commit 2064ab5af2be6c727999285d5d65c09855914e83)
Co-authored-by: Timur Ramazanov <[email protected]>
---
.../src/airflow/ui/src/pages/Asset/AssetLayout.tsx | 3 +
.../Asset/AssetStateStore/AssetStateStore.tsx | 2 +-
.../src/airflow/ui/tests/e2e/specs/asset.spec.ts | 75 ++++++++++++++++++++++
3 files changed, 79 insertions(+), 1 deletion(-)
diff --git a/airflow-core/src/airflow/ui/src/pages/Asset/AssetLayout.tsx
b/airflow-core/src/airflow/ui/src/pages/Asset/AssetLayout.tsx
index ac007410c49..7bd53037598 100644
--- a/airflow-core/src/airflow/ui/src/pages/Asset/AssetLayout.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/Asset/AssetLayout.tsx
@@ -134,6 +134,9 @@ export const AssetLayout = () => {
display="flex"
flexDirection="column"
gap={3}
+ height="100%"
+ minH={0}
+ minW={0}
paddingInlineStart={2} // not 3 because the transparent resize
handle is 1 unit wide
>
<Header asset={asset} />
diff --git
a/airflow-core/src/airflow/ui/src/pages/Asset/AssetStateStore/AssetStateStore.tsx
b/airflow-core/src/airflow/ui/src/pages/Asset/AssetStateStore/AssetStateStore.tsx
index da27f134b42..7fb4d171ce0 100644
---
a/airflow-core/src/airflow/ui/src/pages/Asset/AssetStateStore/AssetStateStore.tsx
+++
b/airflow-core/src/airflow/ui/src/pages/Asset/AssetStateStore/AssetStateStore.tsx
@@ -155,7 +155,7 @@ export const AssetStateStore = () => {
const columns = getColumns({ assetId, translate });
return (
- <Box px={4}>
+ <Box display="flex" flex={1} flexDirection="column" minH={0}
overflow="hidden" px={4}>
<ErrorAlert error={error} />
<DataTable
columns={columns}
diff --git a/airflow-core/src/airflow/ui/tests/e2e/specs/asset.spec.ts
b/airflow-core/src/airflow/ui/tests/e2e/specs/asset.spec.ts
index 2af18527fe5..59adb649653 100644
--- a/airflow-core/src/airflow/ui/tests/e2e/specs/asset.spec.ts
+++ b/airflow-core/src/airflow/ui/tests/e2e/specs/asset.spec.ts
@@ -16,6 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
+import { randomUUID } from "node:crypto";
import { testConfig } from "playwright.config";
import { expect } from "tests/e2e/fixtures";
@@ -71,4 +72,78 @@ test.describe("Assets Page", () => {
await assetDetailPage.verifyProducingTasks();
await assetDetailPage.verifyScheduledDags();
});
+
+ test("asset state store entries remain scrollable when the table overflows",
async ({
+ assetData: _data,
+ assetDetailPage,
+ authenticatedRequest,
+ browserName,
+ page,
+ }) => {
+ test.skip(browserName !== "chromium", "The asset fixture is shared across
browser projects.");
+
+ const assetResponse = await authenticatedRequest.get(
+
`/api/v2/assets?uri=${encodeURIComponent(testConfig.asset.name)}&limit=1`,
+ );
+
+ expect(assetResponse.ok()).toBe(true);
+
+ const { assets } = (await assetResponse.json()) as { assets: Array<{ id:
number }> };
+ const [asset] = assets;
+
+ if (asset === undefined) {
+ throw new Error(`Test asset ${testConfig.asset.name} was not found`);
+ }
+
+ const keyPrefix = `000-e2e-scroll-${randomUUID()}`;
+ const keys = Array.from({ length: 20 }, (_, index) =>
`${keyPrefix}-${String(index).padStart(3, "0")}`);
+
+ try {
+ const responses = await Promise.all(
+ keys.map((key, index) =>
+
authenticatedRequest.put(`/api/v2/assets/${asset.id}/state-store/${encodeURIComponent(key)}`,
{
+ data: { value: { index, value: `scroll test ${index}` } },
+ }),
+ ),
+ );
+
+ expect(responses.every((response) => response.ok())).toBe(true);
+
+ await assetDetailPage.goto();
+ await assetDetailPage.clickOnAsset(testConfig.asset.name);
+ await page.getByRole("link", { exact: true, name: "Asset State Store"
}).click();
+
+ const table = page.getByTestId("table-list");
+ const lastKey = `${keyPrefix}-019`;
+
+ await expect(table.getByText(lastKey)).toBeAttached();
+
+ const canScroll = await table.evaluate((element) => {
+ let parent = element.parentElement;
+
+ while (parent !== null) {
+ const { overflowY } = window.getComputedStyle(parent);
+
+ if ((overflowY === "auto" || overflowY === "scroll") &&
parent.scrollHeight > parent.clientHeight) {
+ parent.scrollTop = parent.scrollHeight;
+
+ return parent.scrollTop > 0;
+ }
+
+ parent = parent.parentElement;
+ }
+
+ return false;
+ });
+
+ expect(canScroll).toBe(true);
+ await expect(table.locator("tbody tr").last()).toBeInViewport();
+ } finally {
+ await Promise.all(
+ keys.map((key) =>
+
authenticatedRequest.delete(`/api/v2/assets/${asset.id}/state-store/${encodeURIComponent(key)}`),
+ ),
+ );
+ }
+ });
});