ryanahamilton opened a new pull request, #71731:
URL: https://github.com/apache/airflow/pull/71731

   Every list page in the UI arranged its own controls above the table, so 
search
   boxes, filter bars, sort selects and "add" buttons landed in a different 
place —
   and at a different size — on each one. The Dags list went further and had a
   filtering implementation entirely of its own.
   
   This makes both consistent.
   
   ## Dags list moves onto the shared `FilterBar`
   
   `src/pages/DagsList/DagsFilters/` was a parallel implementation: eight
   always-visible bespoke controls, its own `chakra-react-select` wrapper, its 
own
   URL read/write and pagination reset. It shared no code with `FilterBar`, 
which
   the other ten list pages use.
   
   Adopting `FilterBar` first required teaching it three things it could not
   express:
   
   - **Repeated URL params.** `useFiltersHandler` only ever called `set()`, so
     `tags`, `teams`, `owners` and `timetable_type` had no representation. This
     also meant the existing `TEAMS` filter could hold a single value even 
though
     `DagRuns` and `TaskInstances` already read it with `getAll()` — a latent 
bug
     this fixes.
   - **Booleans.** Previously faked as a select over `"true"`/`"false"` strings.
     The new `boolean` type activates in one click. Its value is still the 
*string*
     `"true"`: `isValidFilterValue` treats any non-empty value as set, so a real
     boolean would write `needs_review=false` into the URL and reintroduce the
     tri-state ambiguity the type exists to remove.
   - **Async option lists.** Tags and timetable types need search-as-you-type 
over
     a paginated endpoint, which a static `options` array cannot express. These 
are
     supplied via an optional `EditorComponent` on the config, so each editor 
owns
     its query and only runs it while its pill is mounted — an improvement on 
the
     old page, which fired both queries on every render whether used or not.
   
   **Full recording of new FilterBar implementation: 
https://r.hmlt.in/01a0112e-aaff-7774-8ec3-37b930b0f0c5**
   
   Two defects fall out of the move:
   
   - Pills snapshot their config when created, which for URL-seeded filters 
happens
     before the i18n namespaces resolve, so deep-linked pills rendered raw
     translation keys as labels. This affected every `FilterBar` page.
   - The `owners` param filtered the list with no control anywhere — reachable 
only
     by clicking an owner link, and impossible to clear without editing the 
URL. It
     now has a removable pill.
   
   Where `hide_paused_dags_by_default` is set, the default is written into the 
URL
   so it shows as a pill rather than filtering invisibly. It is seeded once per
   page load, so removing the pill is how you ask to see paused Dags.
   
   ### Default state - before:
   
   <img width="1548" height="398" alt="file-f484ee7d48bc2f46fe02362c3c1bd5ad" 
src="https://github.com/user-attachments/assets/96c42b4b-7fd5-4e5d-9e36-1b096905862d";
 />
   
   ### Default state - after:
   
   <img width="1548" height="361" alt="file-6300bdf6d25fe278e94f5024d95e4cd5" 
src="https://github.com/user-attachments/assets/e9a315f3-d344-405d-af31-fe9ae2fa7884";
 />
   
   
   ### Every filter loses its "all" option
   
   Selecting "all" matches everything, which is the same as not filtering. Now 
that
   a filter left unset is dropped from the bar, its absence already says so, and
   offering the choice as well gave two ways to spell one thing — one of which
   still looked like an active filter.
   
   It is gone from every select that had it: run state (last run, any run, and 
the
   shared state filter), paused state, run type, job state, job type, task 
instance
   state, deadline status, and the HITL response state.
   
   | Before | After |
   |--|--|
   |<img width="100%" alt="Zight Recording 2026-08-17 at 03 06 37 PM" 
src="https://github.com/user-attachments/assets/fb34dbb4-3d61-40d6-8c76-8103b0eca3ec";
 /> |<img width="100%"  alt="Zight Recording 2026-08-17 at 03 07 19 PM" 
src="https://github.com/user-attachments/assets/c8db304f-bb15-49f7-8db7-63031d9b6040";
 /> | 
   
   Paused needed more than the option removed. Its "all" existed because 
clearing
   the param let the `hide_paused_dags_by_default` seeding put the filter 
straight
   back, so the option was the only route to paused Dags. Seeding now happens 
once
   per page load, and the list no longer re-applies the default when the param 
is
   absent, which makes removing the pill mean what it says. A fresh load 
re-applies
   the default, which is what a deployment setting should do.
   
   The lists in `stateOptions.ts` keep their "all" entries — the graph task 
filter
   still uses them and is not a FilterBar pill.
   
   ## Table controls move into the table header
   
   `DataTable` gains three slots, each documented with the kind of control it
   expects, because the distinction is otherwise easy to get wrong:
   
   | Slot | Position | Holds |
   |---|---|---|
   | `filterActions` | left, under the heading | changes *which* rows come back 
— `SearchBar`, `FilterBar` |
   | `presentationActions` | right, with the columns menu | changes *how* 
returned rows are drawn — sort selects, expand/collapse |
   | `primaryActions` | right, level with the heading | the page's calls to 
action — "Add Pool", "Add Variable" |
   
   The former `actions` prop is renamed `presentationActions`; the old name 
gave no
   hint which of the three kinds of control belonged in it. Fourteen pages were
   migrated.
   
   Each slot has to be named in the header row's render condition. Missing one 
is
   invisible until a table has nothing else to put in the row — the row count
   heading is suppressed while loading, so the controls would disappear on 
exactly
   the tables that show least. There are regression tests for all three.
   
   ### Notes for reviewers
   
   - **The `TEAMS` single-to-multi conversion is the change most worth a careful
     look.** Blast radius is two pages (`DagRunsFilters`, 
`TaskInstancesFilter`);
     both already read `getAll()` and pass `Array<string>`, and the OpenAPI 
type is
     `Array<string>`. Existing `?teams=x` URLs keep working; the only visible 
change
     is that "All Teams" is replaced by removing the pill. It is separable into 
its
     own commit if preferred.
   - I could not exercise `TEAMS` live — `multi_team` is disabled on the 
instance I
     tested against. It is covered by type-checking and unit tests only.
   - New i18n keys are English-only; the other 20 locales fall back at runtime,
     which is the sanctioned path here (`check-translations-completeness` is a
     warning, and `AGENTS.md` forbids placeholder translations).
   - Dropping the "all" options leaves `dags:filters.allStates`, `allRunTypes` 
and
     the equivalent job keys in place, since `stateOptions.ts` still feeds them 
to
     the graph task filter.
   
   ### Testing
   
   `tsc` and `eslint` clean; 944/944 vitest tests pass, including new coverage 
for
   multiselect round-tripping in `useFiltersHandler`, the boolean and 
custom-editor
   paths in `FilterBar`, all three `DataTable` slots, and filters being dropped 
when
   left unset. Every filter that used to offer "all" was opened in a browser to
   confirm the option is gone. The Dags e2e spec passes
   apart from one failure that reproduces on unmodified `main`. All twelve 
affected
   pages were driven in a browser with no console errors.
   
   ---
   
   ##### Was generative AI tooling used to co-author this PR?
   
   - [X] Yes — Claude Code (Opus 5)
   
   Generated-by: Claude Code (Opus 5) following [the 
guidelines](https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions)
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to