|
| 1 | +--- |
| 2 | +"@object-ui/plugin-grid": patch |
| 3 | +"@object-ui/i18n": patch |
| 4 | +--- |
| 5 | + |
| 6 | +fix(grid): a bulk action's `visible` is evaluated per selected record — objectui#3067 |
| 7 | + |
| 8 | +The selection bar evaluated a def's `visible` against the ambient scope with no |
| 9 | +record bound. That does not fail open, it answers wrongly: with no `record` in |
| 10 | +scope the lenient evaluator returned `true` for **every** row-scoped predicate, |
| 11 | +including the ones that should be false — `${record.done}` and |
| 12 | +`${record.owner == user.id}` both came back `true`. An authored gate was not |
| 13 | +weakened, it was inverted for half its inputs, and nothing distinguished that |
| 14 | +from a real verdict. |
| 15 | + |
| 16 | +`visible` is now evaluated **once per selected record, with that record in |
| 17 | +scope**, fail-closed per record and warning once on a fault — the same contract |
| 18 | +the row kebab uses. One evaluation answers both questions: |
| 19 | + |
| 20 | +- **Is the button offered?** When at least one selected record passes. A |
| 21 | + record-free predicate (`features.x`, `current_user.y`) returns the same |
| 22 | + verdict for every row, so it still behaves as a plain button-level gate — no |
| 23 | + syntactic sniffing for `record` references is involved. |
| 24 | +- **Which records does it run on?** The ones that passed. The confirm step |
| 25 | + states how many were skipped, so a run over fewer records than the user |
| 26 | + ticked says so instead of quietly shrinking the selection. |
| 27 | + |
| 28 | +Eligibility is re-applied to the EXPANDED set after "select all N matching", |
| 29 | +not just the page selection the button could see. |
| 30 | + |
| 31 | +The mechanism predates objectui#3002, but only inline-authored |
| 32 | +`bulkActionDefs[].visible` used to reach it — written by authors who knew there |
| 33 | +was no record. #3031 began promoting object actions into the bar, and their |
| 34 | +`visible` is typically written for a row/record surface, which is what put |
| 35 | +row-scoped predicates in front of a record-free evaluation. |
0 commit comments