xlsx/table-listobject--plain

A worksheet carries a <table> (ListObject) over A1:C5 with a header row of column names and four data rows. Four parameter cases toggle headerRowCount, totalsRowCount, tableStyleInfo/@showColumnStripes.

Library verdicts: python-xlsx: — xlsxjs: —

Metadata

Feature idxlsx/table-listobject--plain
Formatxlsx
Categoryworkbook-structure
Familyxlsx/table-listobject
Axis valuescase=plain
Spececma-376-5-part-1 § 18.5 table

XPath assertions

ID / partPredicateXPathpython-xlsxxlsxjs
table-present-plain
xl/tables/table1.xml
exist
xl/tables/table1.xml must carry a <table> whose @displayName equals 'TestTable'.
//x:table[@displayName='TestTable']
table-variant-plain
xl/tables/table1.xml
equal = A1:C5
The case-specific ListObject attribute must match the expected value.
//x:table[@displayName='TestTable']/@ref

Render assertions

No render assertions declared.

Generator source

scripts/gen_table_listobject.py

#!/usr/bin/env python3
"""Generate ``fixtures/xlsx/table-listobject--<case>.xlsx`` fixtures.

Parameterised generator for the ``xlsx/table-listobject`` manifest.
Four cases drive ``Worksheet.add_table()`` with different toggles:

- ``plain`` — header row only.
- ``with-totals-row`` — ``totals_row={'Price': 'sum'}``.
- ``with-header-row`` — explicit ``header_row=True`` (default; pinned so
  the manifest's equality assertion remains honest).
- ``with-filter`` — ``tableStyleInfo/@showColumnStripes=1`` via
  ``show_col_stripes=True`` and a built-in style.

python-xlsx emits the ListObject at ``xl/tables/table1.xml``.
"""

from __future__ import annotations

import argparse
import datetime as _dt
from pathlib import Path

from xlsx import Workbook

_REPRODUCIBLE_DT = _dt.datetime(2000, 1, 1, 0, 0, 0)
_REPO_ROOT = Path(__file__).resolve().parent.parent

_HEADER = ("Name", "Qty", "Price")
_DATA = (
    ("Apple", 10, 1.5),
    ("Pear", 20, 2.5),
    ("Plum", 30, 3.5),
    ("Kiwi", 40, 4.5),
)


def _seed(ws) -> None:
    """Write the 5-row data block into A1:C5."""
    for j, v in enumerate(_HEADER, 1):
        ws.cell(row=1, column=j, value=v)
    for i, row in enumerate(_DATA, 2):
        for j, v in enumerate(row, 1):
            ws.cell(row=i, column=j, value=v)


def _seed_with_totals(ws) -> None:
    """Seed A1:C6 — header + 4 data rows + totals row placeholder."""
    _seed(ws)
    ws.cell(row=6, column=1, value="Total")


def main() -> int:
    parser = argparse.ArgumentParser()
    parser.add_argument("--case", required=True,
                        choices=("plain", "with-totals-row",
                                 "with-header-row", "with-filter"))
    parser.add_argument("--out", required=True)
    args = parser.parse_args()

    out_path = Path(args.out)
    if not out_path.is_absolute():
        out_path = _REPO_ROOT / out_path

    wb = Workbook()
    ws = wb.active
    ws.title = "Sheet1"

    if args.case == "plain":
        _seed(ws)
        ws.add_table("A1:C5", name="TestTable", display_name="TestTable")
    elif args.case == "with-totals-row":
        _seed_with_totals(ws)
        ws.add_table("A1:C6", name="TestTable", display_name="TestTable",
                     totals_row={"Price": "sum"})
    elif args.case == "with-header-row":
        _seed(ws)
        ws.add_table("A1:C5", name="TestTable", display_name="TestTable",
                     header_row=True)
    elif args.case == "with-filter":
        _seed(ws)
        ws.add_table("A1:C5", name="TestTable", display_name="TestTable",
                     style="TableStyleMedium9", show_col_stripes=True)

    out_path.parent.mkdir(parents=True, exist_ok=True)
    wb.save(out_path, zip_date_time=_REPRODUCIBLE_DT)
    print(out_path)
    return 0


if __name__ == "__main__":
    raise SystemExit(main())

Manifest (expanded)

{
  "$schema": "../manifest.schema.json",
  "id": "xlsx/table-listobject--plain",
  "kind": "literal",
  "title": "ListObject (structured table) — 4 toggle variants",
  "format": "xlsx",
  "category": "workbook-structure",
  "summary": "A worksheet carries a <table> (ListObject) over A1:C5 with a header row of column names and four data rows. Four parameter cases toggle headerRowCount, totalsRowCount, tableStyleInfo/@showColumnStripes.",
  "spec": {
    "source": "ecma-376-5-part-1",
    "clause": "18.5",
    "element": "table",
    "rnc_reference": "spec/ecma-376-5/part-1/rnc/SpreadsheetML.rnc",
    "xsd_reference": "spec/ecma-376-5/part-1/xsd/sml.xsd",
    "notes": "A <table> (ListObject) part lives at xl/tables/tableN.xml, referenced from a worksheet via a table-relationship. @displayName identifies the table in the UI; @ref is the cell-range the table covers. Four variants exercise the common toggles: 'plain' is a vanilla header-only table; 'with-totals-row' adds @totalsRowCount=1 and a TableColumn/@totalsRowFunction aggregate; 'with-header-row' pins the default @headerRowCount=1 explicitly so the assertion is an equality rather than presence check; 'with-filter' adds a <tableStyleInfo> with @showColumnStripes=1. python-xlsx's Worksheet.add_table() drives all four shapes via the same call signature with different kwargs; the emitted part uses the default SpreadsheetML namespace (XPath binds 'x')."
  },
  "fixtures": {
    "machine": "xlsx/table-listobject--plain"
  },
  "generator": {
    "python": "scripts/gen_table_listobject.py",
    "arg_template": "--case {case.id} --out fixtures/xlsx/table-listobject--{case.id}.xlsx"
  },
  "_expansion": {
    "parent_id": "xlsx/table-listobject",
    "bindings": {
      "case": "plain"
    }
  },
  "assertions": [
    {
      "id": "table-present-plain",
      "part": "xl/tables/table1.xml",
      "namespaces": {
        "x": "http://schemas.openxmlformats.org/spreadsheetml/2006/main"
      },
      "xpath": "//x:table[@displayName='TestTable']",
      "must": "exist",
      "description": "xl/tables/table1.xml must carry a <table> whose @displayName equals 'TestTable'."
    },
    {
      "id": "table-variant-plain",
      "part": "xl/tables/table1.xml",
      "namespaces": {
        "x": "http://schemas.openxmlformats.org/spreadsheetml/2006/main"
      },
      "xpath": "//x:table[@displayName='TestTable']/@ref",
      "must": "equal",
      "value": "A1:C5",
      "description": "The case-specific ListObject attribute must match the expected value."
    }
  ]
}

Fixture

Download table-listobject--plain.xlsx (5.5 KB)

Reference preview

No rendered reference is available for this case.

Spec notes

A <table> (ListObject) part lives at xl/tables/tableN.xml, referenced from a worksheet via a table-relationship. @displayName identifies the table in the UI; @ref is the cell-range the table covers. Four variants exercise the common toggles: 'plain' is a vanilla header-only table; 'with-totals-row' adds @totalsRowCount=1 and a TableColumn/@totalsRowFunction aggregate; 'with-header-row' pins the default @headerRowCount=1 explicitly so the assertion is an equality rather than presence check; 'with-filter' adds a <tableStyleInfo> with @showColumnStripes=1. python-xlsx's Worksheet.add_table() drives all four shapes via the same call signature with different kwargs; the emitted part uses the default SpreadsheetML namespace (XPath binds 'x').