Skip to content

[pull] master from useblocks:master - #220

Merged
pull[bot] merged 4 commits into
boschglobal:masterfrom
useblocks:master
Sep 8, 2026
Merged

pull[bot] merged 4 commits into
boschglobal:masterfrom
useblocks:master

Conversation

@pull

@pull pull Bot commented Sep 8, 2026 •

Copy link
Copy Markdown

See Commits and Changes for more details.


Created by pull[bot] (v2.0.0-alpha.4)

Can you help keep this open source service alive? 💖 Please sponsor : )

chrisjsewell and others added 4 commits September 8, 2026 19:04
…need_func` role (#1919)

Since 4.0.0 (#1269, #1266) every `[[...]]` inside a need's **content**
has been executed
*and* warned about under `[needs.deprecated]`, with the `:ndf:` role as
its replacement;
the `need_func` role was deprecated in the same round. Four majors have
carried the
warning, and 9.0.0 is the release that drops it.

After this PR:

- `[[...]]` in a need's **content** is plain text, as it is in any other
Sphinx document.
- `:ndf:` is the one way to run a dynamic function inline.
- a document that still writes `:need_func:` gets docutils' own
`Unknown interpreted text role "need_func"`. No stub role warns: the
point of the major
  is that the old syntax is gone.
- the private `_detect_and_execute_field` goes too — a pure deletion in
its own commit; it
has been uncalled since #1516 replaced its two call sites with the
parsed
  `DynamicFunctionParsed` path, and it was never re-exported.
- `[[...]]` in **fields** is untouched — `:tags:`, `:status:`,
`:links:`, `:style:`,
`needtable`'s `:style_row:`, extra fields, `needs_global_options`
defaults. That is still
the documented way to write a dynamic function in an option, and
`FUNC_RE` and
  `check_and_get_content` are exactly as they were.

`find_and_replace_node_content` now does one thing: replace every
`NeedFunc` node with the
text its function returns, skipping nested needs as before — and a
`reference`'s children
are now walked like everything else's, which resolves an `:ndf:` reached
through a
substitution used as an internal hyperlink (`|sub|_` with ``.. _sub:
`Section`_``), where
the old code returned early on the missing `refuri` and left `??` plus a
spurious
`Error while executing function 'copy': Need not found`. Gone with the
scan are the quote
normalisation it needed (sphinx's smartquotes transform had already
curled `"` to `“` by
the time it ran) and the rewrite of a link's `refuri`, so
`` `link <http://www.[[copy('id')]]>`_ `` is now an ordinary URL.

Beyond the deprecation itself, the scan intercepted `[[...]]` in a
need's body before roles
were resolved, so the `:need:` role's own `[[field]]` template syntax
could not be written
inside a need at all. That is what blocks #1201, which can be updated
and merged once this
lands.

## Tests

`doc_dynamic_functions` and `needextract_with_nested_needs` keep their
`[[copy("id")]]`
content lines, and the tests now assert positively that they render as
written rather than
only that the warning is absent; the `:ndf:` lines beside them still
resolve. In
`doc_df_user_functions` the four content calls become `:ndf:` roles,
which keeps every
error path that project exists to cover (bad return type, "Not a
function call", unknown
function), with one plain `[[...]]` line added for the literal
assertion.
`test_need_func_role_removed` is new, and so are fences for two paths
that had none: the
`|sub|_`-into-an-internal-link shape above, and `needtable`'s
`:style_row: needs_[[copy("status")]]` — the one live consumer of
`check_and_get_content`,
which until now could be reduced to a pass-through with the whole suite
and the docs build
still green.

## For the 9.0.0 changelog

**BREAKING**: `[[...]]` inside a need's content is no longer interpreted
as a dynamic
function, and the `need_func` role is removed; both were deprecated in
4.0.0. Use the
`:ndf:` role. Dynamic functions in fields (`[[...]]` in options and
links) are unchanged.

Closes #1906
…1201)

Both examples on the roles page — the `:need:` one and the
`need_outgoing` one — become the content of the need they describe,
rather than paragraphs after it.

Until #1919 this could not build: a `[[field]]` inside a need's content
was intercepted as the deprecated dynamic-function syntax (4.0.0, #1269)
before the `:need:` role that carries it was resolved, so the first
example's `[[value]] [[unit]] …` line failed the `-W` docs build with
ten warnings once it sat inside the need. #1919 removed that scan for
9.0.0 (#1906), and the `:need:` role's own template syntax now renders
inside a need — that build was the acceptance test for #1919, with this
branch's diff applied.

The change itself is the original four lines; the branch was rebased
after the documentation moved under `packages/sphinx-needs/docs/`, and
brought up to date with `master` once #1919 landed.

---------

Co-authored-by: Chris Sewell <chrisj_sewell@hotmail.com>
…ancer (#1920)

A `needtable` with the default `:style: datatables` is still interactive
— sort a column, search the whole table, page through it, choose how
many rows a page holds, switch columns off, copy it or download it as
CSV — but it is now Sphinx-Needs' own script instead of a vendored copy
of DataTables 1.10.16 from 2018, and it only loads on pages that
actually have a table. The enhancement happens **in place**: every
`<tr>` and `<td>` Sphinx-Needs rendered is still the same element
afterwards, so `:style_row:` classes, the `needs_<column>` cell classes,
the links inside cells and the `<colgroup>` all survive sorting and
searching, and a reader with JavaScript off still gets the whole table.
The script is deferred, so it never blocks the parser.

**4.72 MB of vendored JavaScript in 34 files becomes 58.5 KB in three; a
page with a table loads 55.9 KB (14.8 KB gzipped) instead of 2.29 MB
(997 KB gzipped), and `search.html`, `genindex.html` and every page
without a table now load none of it** (closes #462 for the table
assets).

### Why not Grid.js, and why not DataTables 3

Grid.js — what #1464 proposed — reads `td.innerHTML` into a
`string[][]`, hides the source table and offers one class string for
*all* rows, so `:style_row:` cannot survive it by construction; it has
had no release since March 2024. DataTables 3 is jQuery-free, in-place
and MIT, and was the honest fallback at about 67 KB gzipped; it was not
chosen because it brings its own control DOM and CSS with fourteen
`!important` rules that every host theme has to fight, it is a
six-week-old major from a one-person project, and any third-party bundle
needs a build toolchain and a manifest in this repository that a
hand-written file does not. The full comparison — simple-datatables
(LGPL-3.0), Tabulator, List.js, tablesort, TanStack — is the decision
record at the top of
`packages/sphinx-needs/design/needstable-contract.md`.

The claims in this description were re-measured in review on two
independent corpora: 41.1 ms and 40.2 ms to initialise ten thousand rows
against our 41.5 ms, and 10 000 of 10 000 rows, 60 000 of 60 000 cells
and 6 of 6 `<col>` elements still the same DOM objects after five sort,
filter and destroy cycles.

### The markup is now a contract

`<table>` keeps `NEEDS_DATATABLES` as its hook and gains
`data-needstable-*` options; every `<th>` gains `scope="col"`, a
`data-col` column key and, where Sphinx-Needs knows it, a `data-type`;
every `<tr>` gains `data-need-id`, and a part row `data-parent`.
`:show_filters:` now writes its paragraph *after* the table instead of
between `</tbody>` and `</table>`, which was invalid HTML. The design
document specifies all of it — the markup, the script's API and
behaviours, the CSS custom properties a host answers, and the vendoring
rule — so that any producer of needtable markup can emit against it.
ubCode, useblocks' Rust-based tooling for needs projects, renders
needtables from the same sources and will ship this asset unchanged
against the same markup contract.

The script and its structural stylesheet,
`src/sphinx_needs/libs/html/needstable.{js,css}`, are plain ES2020
JavaScript type-checked through JSDoc (`// @ts-check`; the one-command
`tsc` recipe is in the design document) and host-agnostic CSS that asks
for every colour through a `--needstable-*` custom property.
Sphinx-Needs' own theme sheets answer those properties; a consumer
without them gets `currentColor` fallbacks.

### Two visible behaviour changes

The count under the table reads *Showing 1–10 of 42* and counts
**needs**, not rows: a need and its parts are one entry, so a page never
splits a need from its parts, and a table with `:show_parts:` reports a
smaller total than DataTables did. And a cell with no value now sorts
**last** whichever way the column points, where DataTables sorted blanks
first.

### Removed, and what replaces them

- **Excel and PDF export** → the CSV download (UTF-8 with a BOM, so
Excel opens it directly) and the browser's own print command. They were
about 900 KB of the payload for two buttons.
- **Column reordering** → nothing. It was switched on, undocumented, and
no issue ever mentioned it.
- **`.dataTables_*` / `.dt-button` CSS hooks** → the `needstable-*`
classes. A project that styled the old chrome has to re-point at the new
one; a project that styled `.NEEDS_DATATABLES`, `.need`, `.need_part` or
`needs_<column>` is unaffected.
- **The `--sn-color-datatable-*` theme variables** →
`--sn-color-table-{label,btn-border}`.

Upgrading needs nothing else: this release changes four of the
extension's own asset files, and they are now re-copied over a build
directory that still holds the old ones. Sphinx would otherwise leave
the stale stylesheet in place with a `copy_overwrite` warning — and fail
a `-W` build — on the first incremental build after the upgrade.

### Gained

- a page-size control that includes *All* (a review finding on #1464),
configurable through `needs_table_page_size`, `needs_table_page_sizes`
and a `:page_size:` option;
- typed sorting that returns to the server's `:sort:` order on the third
click, so the documented "sorting only works with `:style: table`"
caveat is gone;
- part rows that travel with their need through sorting and filtering;
- `aria-sort` and keyboard-operable headers, an `aria-live` result
count, focus that stays inside the pager when you page with the
keyboard;
- a scroll frame of the widget's own, so a wide table scrolls inside it
instead of escaping the page, and a table that fits fills its column;
- the columns control is a plain button in every theme we render, rather
than whatever the theme does to a `<summary>`;
- twenty-one browser tests and a markup-contract test module where there
were none.

jQuery is still a dependency, because `sphinx_needs_collapse.js` (the
need meta box, not the table) is still jQuery — porting it is the
follow-up that lets `sphinxcontrib-jquery` go.

### Render pass

The docs were built with furo, sphinx_rtd_theme, pydata-sphinx-theme,
sphinx-immaterial and alabaster, in light and dark where the theme has
one, before and after, with a new `poe docs-needs-shots` instrument (its
own PR follows). The Read the Docs preview of this PR shows the furo
build live.

### Changelog draft

```rst
* :ref:`✨ Improvement <needtable>`: The ``datatables`` table style is now Sphinx-Needs' own,
  dependency-free enhancer instead of the bundled `DataTables <https://datatables.net>`__
  package. It sorts (typed, and back to ``:sort:`` order on the third click), searches, pages
  with a 10/25/50/All control, hides columns, copies and downloads CSV -- and it enhances the
  rendered table in place, so ``:style_row:``, the ``needs_<column>`` cell classes and the
  links inside cells survive every interaction. Need parts stay with their need. New:
  :ref:`needs_table_page_size`, :ref:`needs_table_page_sizes` and
  :ref:`page_size <needtable_page_size>`. (:pr:`1920`)
* :ref:`✨ Improvement <needtable>`: The table's script and stylesheet are added only to pages
  that contain a needtable; ``search.html`` and ``genindex.html`` no longer carry them.
  4.72 MB of vendored JavaScript becomes 58.5 KB. (:issue:`462`, :pr:`1920`)
* :ref:`👌 Improvement <needtable>`: The interactive table's count reads *Showing 1-10 of
  42* and counts **needs**: a need and its :ref:`parts <needtable_show_parts>` are one
  entry, so a page never splits a need from its parts. A table with ``:show_parts:``
  therefore reports a smaller total than the DataTables style did, which counted rows.
  Cells with no value now sort last whichever way the column points, where DataTables
  sorted them first. (:pr:`1920`)
* 💥 Removed: Excel and PDF export, and column reordering, from the ``datatables`` style. The
  CSV download opens in Excel and browsers print; column reordering was undocumented.
  Projects that styled ``.dataTables_*`` or ``.dt-button`` must re-point at the
  ``needstable-*`` classes, and ``--sn-color-datatable-*`` is now ``--sn-color-table-*``.
  (:pr:`1920`)
```

Closes #462.

### Related issues, and what this changes for each

- **#1464** — superseded by this PR; the size measurement, the deletion
of the dark-theme DataTables block and the 10/25/50/All requirement were
kept from it.
- **#408** (per-table DataTables configuration) — the requested shape, a
dict of raw DataTables options, no longer applies. Per-table settings
now travel as `data-needstable-*` attributes emitted from directive
options and config (page size and page sizes ship here); more knobs
follow the same route. Note that the side extension posted there in
August reconfigures DataTables and stops working with this change, and
PDF export is replaced by CSV plus the browser's print dialog.
- **#1425** (named table layouts) — orthogonal server-side presets;
nothing in the contract stands in their way. Follow-up.
- **#771** (Read the Docs theme) — the collision was between DataTables'
wrapper and the theme's own table wrapper; the DataTables wrapper is
gone and the widget's scroll frame is the container, with the theme's
wrapper nesting inside it. Measured correct on the docs build; a plain
project on that theme still wants confirmation, because these docs carry
a small RTD override of their own.
- **#1144** (order of appearance) — `:sort: lineno` already answers it,
but only worked with `:style: table` because DataTables re-sorted at
load. The interactive table now keeps the server's `:sort:` order as its
initial and "unsorted" state, so it works in the default style too.
Multiple `:sort:` keys remain a follow-up.

### In short: issues and follow-ups

| | after this PR |
|---|---|
| #462 | closed for the table assets; the shared host stylesheet is
still global — follow-up |
| #1464 | superseded; close with the measurement that Grid.js re-renders
from cell text |
| #1144 | `:sort: lineno` now works in the default style; close after
release, multi-key `:sort:` stays open |
| #771 | DataTables' wrapper gone, frame + theme wrapper nest cleanly on
the docs build; confirm on a plain RTD project, then close |
| #408 | answered by `data-needstable-*` attributes rather than a
DataTables option dict; the August side extension there stops working —
needs a heads-up |
| #1425 | orthogonal server-side presets; unaffected, follow-up |

Follow-up work, none blocking: port `sphinx_needs_collapse.js` off
jQuery and drop `sphinxcontrib-jquery`; gate the shared host stylesheet
per page; type-check the shared asset in CI; a sticky header now that
the frame is an explicit box; a caption-derived CSV file name; logical
instead of physical padding for the sort glyph under RTL; and, on
request, column reorder, multi-column sort, responsive collapse, URL
state or translation catalogues inside the script.
`needstable.js` (#1920) is plain JavaScript typed through `// @ts-check`
and JSDoc, and `design/needstable-contract.md` §4 records the single
command that checks those types. It passes at the tip, but nothing ran
it, so any later edit could regress a type silently. This makes it a
gate.

- **A poe task, `typecheck-js-needs`**, holds the command with the
typescript version **pinned** (`typescript@5.9.3`): an unpinned `npx -p
typescript` resolves whatever compiler is newest that day, which would
let a TypeScript release turn a pull request red with nothing here
having changed. To bump it, edit that one literal and run the task.
5.9.3 is the newest 5.9.x; the file is also clean under 7.0.2, today's
`latest`.
- **One Lint step runs the task**, not a copy of the command, so the
gate a developer runs locally and the gate that blocks a pull request
cannot drift, the same arrangement as the prek hooks and `poe lint`.
Lint rather than the `tests-js` lane: it needs no browser, no sphinx and
no interpreter matrix, and Lint is a required status context in its own
right. No `setup-node`: `ubuntu-latest` ships node and npm, and `npx -y
-p` fetches the pinned compiler into the runner's npm cache for the run
(about 2.5 s cold on an empty cache, 23 MB unpacked).
- **Files checked**: `needstable.js` only. `sphinx_needs_collapse.js` is
jQuery-era, carries no `// @ts-check` and does not pass `--strict`;
#1922 ports it and is the pull request that adds the second line. The
task lists one path per line, so that is a one-line change.
- **Docs**: §4's "It is not a CI gate here" sentence now says the
opposite, and the root `AGENTS.md` commands block gains the task. No
changelog entry: nothing user-visible changed.

Proof the fence bites: with `this.sortColumn = -1` (declared `@type
{number}`) changed to a string, the task exits 2 with

```
src/sphinx_needs/libs/html/needstable.js(452,13): error TS2322: Type 'string' is not assignable to type 'number'.
```

Closes #1924
@pull pull Bot locked and limited conversation to collaborators Sep 8, 2026
@pull pull Bot added the ⤵️ pull label Sep 8, 2026
@pull
pull Bot merged commit 4a54ea2 into boschglobal:master Sep 8, 2026
3 checks passed
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants