Skip to content

GTS, v2... vite. - #1256

Open
betocantu93 wants to merge 54 commits into
Addepar:masterfrom
prysmex:spike/latest-blueprint-gjs
Open

betocantu93 wants to merge 54 commits into
Addepar:masterfrom
prysmex:spike/latest-blueprint-gjs

Conversation

@betocantu93

@betocantu93 betocantu93 commented Oct 8, 2026 •

Copy link
Copy Markdown

This was generated with AI, all tests are passing... GTS, types, glimmer components... etc...

DEMO docs
https://ember-table-ten.vercel.app/docs

These re-exports and classic pod components were renamed to .gjs without
content changes; .gjs signals a <template> module, which they are not.
Replace the push-based syncing that the GJS migration layered on with
idiomatic Octane patterns:

- Share table state through a tracked TableApi instead of a mutated POJO,
  removing the thead -> tbody registerBody/updateSorts channel.
- Let ColumnTree and CollapseTree derive their inputs from component
  getters (readOnly aliases on @dependentKeyCompat), removing didUpdate
  syncing, notifyPropertyChange calls, next() deferrals and the
  layout/selection/interaction revision counters.
- Model cell and row APIs as plain classes with getters over the row's
  args instead of EmberObjects with defineProperty and observers.
- Remove syncResizedColumnElements from ColumnTree. Body cells were never
  restyled because ember-td's modifier used `?.`, which Glimmer parses as
  a property literally named `columnMeta?`.
- Attach scroll indicator listeners to the overflow element via modifiers
  rather than a hidden placeholder element and getElementById.
- Restore master behavior: tfoot rows come from the collapse tree,
  header contextmenu prevents the native menu, cell styles reapply when a
  column's fixed side changes, and @Class is honored on table components.
- Fix loading-more detaching listeners from the wrong element and the
  footer observer's attributeFilter typo.
Pass @Class to EmberTr rather than overriding its internal customClass
getter.
Restructure the repository into a pnpm workspace following the Embroider
addon blueprint:

- ember-table/: the published v2 addon, built with Rollup. Components are
  .gts with Glint signatures and declarations are generated from source,
  replacing the hand-written types/. The classic column and collapse tree
  models stay JavaScript, typed at their boundary.
- test-app/: an Embroider/Vite app running the existing test suite.

Globals injected by the v1 build are now imports; hammerjs is loaded with
importSync so evaluating the addon stays FastBoot-safe. The table component
imports the addon CSS, and the default theme ships as ember-table/default.scss.

Shared section logic moves into a TableSection base so the footer no longer
subclasses the body. `onSelect` now types its `{ abort }` details, and yielded
cell values stay usable when the row type cannot be inferred.

The ember-cli-addon-docs site is removed: it does not build on current Ember.

CI runs lint (including ember-tsc), tests, and @embroider/try scenarios applied
to both packages, so the addon never resolves a second copy of ember-source.
The polyfill positions sticky header and footer cells on the next animation
frame, which test helpers did not wait for, so assertions could run before the
cells were positioned. Register that frame with @ember/test-waiters (a no-op in
production builds). This removes an intermittent failure in the polyfill tests
and the same race in applications' tests.
Port the guides from ember-cli-addon-docs to Docfy Markdown in test-app/docs,
served by the Vite test app. Each example is a self-contained gjs component
using the addon's public import paths, so the displayed source is the code that
runs. The sidebar is generated from Docfy's page metadata.

The docs acceptance tests, which were always skipped because addon-docs was
not installed, now visit every page in the navigation and exercise the
subcolumn and two-state sorting examples. They are skipped below Ember 6.5,
which the docs app requires.

Also document the v2 package (install, default theme, TypeScript and Glint
usage, development) in the README and update SPIKE.md.
ember-classy-page-object is an unmaintained v1 addon, so a v1-free app could
not use ember-table's page objects. Its class-based API (about 200 lines over
ember-cli-page-object, which is already a v2 addon) now lives in
src/test-support/-private as TypeScript, with its MIT notice.

`ember-table/test-support` also exports the page object helpers (PageObject,
collection, findElement, hasClass, ...) so consumers can extend TablePage
without depending on the old addon.

`collection` no longer deletes `scope` from the definition it is given; it
copies the definition's property descriptors, so getters are not evaluated
when the page object is defined.
Strict-mode templates import their components, so
`import { EmberTable, EmberThead, ... } from 'ember-table'` is the natural
API for `.gjs`/`.gts` apps. The deep `ember-table/components/*/component`
paths keep working.
The tests rendered `hbs` templates, which resolve `<EmberTable>` and friends
through the classic resolver and the addon's app re-exports. Strict-mode
`<template>`s import them instead, so the tests no longer need either.

The templates read the test context through a lexical `ctx` binding. Ember's
`set()` dirties the tag a template consumes when reading a plain object's
property, so `this.set(...)` still re-renders. The one curly-invoked table
uses `<:default>`/`<:else>` named blocks.
ember-source is a v2 addon from 6.1. Older versions only work through
Embroider's compat layer, which the test app is dropping. 5.12 is past its
LTS window, and 6.4 is the oldest LTS the test app can run, so it becomes the
floor and the oldest try scenario.

BREAKING CHANGE: ember-table now requires ember-source 6.4 or newer.
The test app was an Embroider compat build: ember-cli-build.mjs, the classic
resolver over compat modules, config/environment via a meta tag, and
@embroider/compat in Vite and Babel. Now it is plain Vite with the `ember()`
plugin:

- `ember-strict-application-resolver` (RFC 1132) resolves only the modules
  `app/app.ts` lists: the router, routes, templates, and the Docfy and
  page-title services.
- The router takes `rootURL` from Vite's `BASE_URL` and uses the `none`
  location under test, replacing config/environment.
- Babel uses `@embroider/macros` directly. The test entry is only built
  outside production mode, so a production build ships just the docs app.
- ember-cli, @embroider/compat, ember-resolver, ember-load-initializers,
  config-meta-loader, optional-features and @embroider/router are gone.

ESLint now covers the `.ts`/`.gts` files. ember-eslint-parser reports
variables read only by a <template> as "only used as a type", so that rule
is off for `.gts`, where type-checking will catch unused locals.

Tested on Ember 7.3 (238 pass, 2 skipped) and 6.4 (232 pass, 8 skipped; the
docs tests need Ember 6.5).
The ColumnTree and CollapseTree models move from `EmberObject.extend({...})`
to native classes, still classic: the same computed properties, dependent
keys, observers and `get`/`set` calls, now typed. Two details keep behavior
identical:

- Values passed to `create()` are applied after class fields, so fields
  like `isSlack = false` keep the old prototype defaults without overriding
  `create({ isSlack: true })`. Inputs and dynamically assigned properties use
  `declare`, so they emit nothing.
- Native computed setters cannot return a value. Ember then caches the
  getter's result, which equals what the classic `width` and `isCollapsed`
  setters returned in every branch.

The header and body extend the models with `readOnly` aliases as native
subclasses. Components still see the models through the interfaces in
`types.ts`.

Also converted: the utilities, the sticky polyfill and test-support. Along
the way:
- `closest()` uses `Element.closest` instead of a vendor-prefix shim.
- The column tree's debug-only check uses `isDevelopingApp()` instead of
  `@glimmer/env`.
- `utils/default-to.js` (unused since the Glimmer migration) and the
  `utils/ember.js` re-export are removed.
ember-test-selectors is a v1 addon, so it can't take part in a Vite build.
Its maintainers publish the transform separately as `strip-test-selectors`,
for apps that own their Babel config. The test app runs it when Babel's env
is `production`, i.e. `vite build`. The test build (`--mode development`)
and the dev server keep the attributes.

Because ember-table and Docfy ship uncompiled templates, the app's transform
strips theirs too. The production bundle goes from 56 `data-test-*` strings
to the 2 that are part of example source shown in the docs, and a rendered
production page has no `data-test-*` attributes. The README shows consumers
the same setup.
The examples imported `ember-table/components/*/component`. They now use the
root exports (`import { EmberTable } from 'ember-table'`), the documented API
for strict-mode apps. The testing guide's links point at the TypeScript
sources.
`vercel.json` at the repository root installs the pnpm workspace (whose
`prepare` builds the addon), builds the test app in production mode and
serves `test-app/dist`: the docs app only, with test selectors stripped.

- pnpm is pinned to the `packageManager` version with `npx pnpm@11.28.2`.
  Vercel would otherwise pick pnpm from the v9 lockfile and miss the pnpm 11
  workspace settings.
- Node is pinned to 22 through `engines`, matching mise.toml.
- Unknown paths rewrite to `index.html`, so deep links load and the app's
  not-found route handles real 404s. Hashed `/assets` are cached immutably.
- `public/404.html`, the GitHub Pages SPA redirect hack, is removed. It was
  already inert, since the new `index.html` lacks its decoding half.
Every app and test module is now .ts/.gts, type-checked by Glint
(`pnpm lint:types`, run by `pnpm lint`).

- tsconfig extends @ember/app-tsconfig, with `noUnusedLocals` on: ESLint's
  no-unused-vars is off for .gts (ember-eslint-parser misreads template
  references), so the type checker reports unused locals. `test-app/*` is
  mapped to `app/*`, matching package.json#exports. Docfy's generated
  templates are excluded.
- `types/` holds ambient declarations: Glint's template types, `rsvp`
  (ember-source ships none), and `ember-table/test-support`, whose export
  lacks a `types` condition.
- Tests type their context as `TableTestContext` (tests/helpers), a
  RenderingTestContext that allows any property, since tests `this.set()`
  arbitrary table arguments. `PageObject` names the untyped page-object
  values. Callbacks are annotated with `this: TableTestContext` only where
  they use `this`.
- Unused positional block params are prefixed with `_`. The scenario's
  `eq` helper is now `isEqual`, and generate-table's `fn` import carries a
  @ts-expect-error: on Ember 7.1+ Glint compiles both as built-in keywords,
  but the app still runs on Ember 6.4.
- Template lines that hit ember-table signature gaps (EmberTh's @api from
  EmberTr, EmberTr's @api from EmberTbody/EmberTfoot, app-defined meta
  properties, EmberTbody without @rows) and the tests' deliberately odd
  arguments carry `@glint-expect-error` comments with the reason.

ESLint lints .ts/.gts with type information. In tests, the no-unsafe-*
rules are off because page objects and the test context are `any`, and
ember/no-runloop stays off as it was for the JS tests.
Converting the test app to TypeScript showed that the documented explicit
composition failed Glint for consumers:

- `<EmberTr @api={{b}}>` with a body section's yield: rows wanted the internal
  row meta, while sections yield the public `TableRowMeta`. Row APIs now use
  `TableRowMeta`, which gains the `select()` and `toggleCollapse()` methods
  it has at runtime. Section yields also carry the row as `api`.
- `<EmberTh @api={{r}}>` with a header row's yield: `EmberTr` always yielded
  body cells. `EmberTr` is now generic over its `@api`: header rows yield
  header cells, and body rows yield body cells with the row type inferred.
  The header cell hash carries `api`, and `EmberTh` accepts either form, as
  `EmberTd` already did. This replaces `EmberTr`'s `<RowType, ColumnType,
  CellComponentType>` parameters with `<Api>`.
- Row, column and cell metas accept app-defined properties, as the Table
  Meta Data guide uses them. Cell metas are typed `TableCellMeta`.
- `<EmberTbody>` `@rows` is optional; it already defaulted to no rows.
- `ember-table/test-support` has a `types` condition, and the mouse helpers'
  coordinates are optional.

The test app's matching `@glint-expect-error` markers and its test-support
shim are removed. Two unit tests are adjusted to the newly typed modules.
@betocantu93 betocantu93 changed the title Spike/latest blueprint gjs GTS, v2... vite. Oct 8, 2026
The demo rendered 11 rows that all fit in the table, so nothing was ever
occluded, and every row had the same `@key` value. It now renders 10,000
rows keyed by a unique `id`, and shows which rows are on screen through
`@firstVisibleChanged`/`@lastVisibleChanged`.

A docs acceptance test checks that only the visible rows are in the DOM,
before and after scrolling to the end.
Code blocks and demo snippets are highlighted at build time with Shiki.
`gjs`/`gts` fences use the glimmer-js/glimmer-ts grammars (the TextMate
grammars of the VS Code Glimmer extension), so `<template>` contents are
highlighted as templates.

Docfy runs rehype plugins synchronously, so the highlighter is created up
front and passed to `@shikijs/rehype/core`. Docfy's Vite plugin escapes `{{`
after rehype plugins run, so highlighted templates still compile.
Every public component now has a description, and every argument and
yielded value a doc comment, with `@default` tags matching the defaults
the components apply. Editors show them on hover, and the API reference
is generated from them.

`@checkboxSelectionMode` gets its own description; it had repeated the one
for `@rowSelectionMode` (as in the v1 addon). `@onSelect` documents its
`{ abort }` argument, and `@onUpdateSorts` that sorting is only enabled
when it is passed.
`test-app/scripts/api-docs.mjs` reads ember-table's declarations with the
TypeScript compiler and writes a page per public component to `docs/api/`:
description, arguments (type as written, required, default, description),
yielded values and root element. It runs when the Vite config loads, so the
reference always matches the built addon; the pages are gitignored.

Docfy lists them under "API Reference". Markdown tables get basic styles,
scoped so the demo tables are unaffected.
Editors could not navigate `.gts` code, for two reasons:

- VS Code's TypeScript server doesn't understand `.gts` without Glint's
  plugin, so jumping from a template landed on the `import` line at best.
  `@glint/tsserver-plugin` is now installed (root and both packages, as the
  Ember blueprints do). `.vscode` recommends the Glint 2 extension and pins
  the workspace TypeScript, since server plugins only load in the JS-based
  TypeScript server.
- Jumping from the test app into the addon went through its declaration
  maps, which point into ember-tsc's generated code, not the `.gts` files.
  The test app now references the addon as a TypeScript project
  (`composite`), so the editor follows `ember-table` imports into its
  source. Type checking still uses each project's own settings.

Checked by driving tsserver with the plugin: definitions from test-app
templates and imports, and from addon templates, land on the right
declarations in source.
@tniezurawski

Copy link
Copy Markdown

If one PR is too scary to go through, Phorest (@Tobzzy to be specific) is now on an initiative to modernize ember-table. We are working through our fork and plan to push the updates here as well. Of course, we will need Addepar to accept those.

/cc @mixonic

@betocantu93

Copy link
Copy Markdown
Author

Im happy either way, my friend! hello from Mexico ✌🏻 hope you came back home safe from EmberFest.

The v1 addon's `defaultTo` only replaced `undefined`, but the `??` getters
also replaced `null`. `@sortFunction={{null}}` used to turn off client-side
sorting and instead fell back to `sortMultiple`. Defaults now apply only to
`undefined` again, and `sortFunction` accepts `null` in its type.

The thead block also yields `api`, matching the body row, so a custom row
component can take `@api={{h.api}}`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@tniezurawski

Copy link
Copy Markdown

Ah, I didn't notice it was you Alberto! Greetings from Poland. All good after Emberfest; I hope the same for you!

I'm happy either way as well. I hope we can get this moving 🚀

betocantu93 and others added 3 commits October 8, 2026 17:26
The copy plugin wrote it during the build, and `addon.clean()` then removed
it as a file the bundle did not emit, so the `./default.scss` export pointed
at a missing file. Copy after the bundle is written instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The addon uses nothing newer than 5.12 and runs in a 5.12 ember-cli app.
6.4 was the oldest Ember the Vite test app can run, which still bounds CI.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants