diff --git a/.bowerrc b/.bowerrc deleted file mode 100644 index 959e1696e..000000000 --- a/.bowerrc +++ /dev/null @@ -1,4 +0,0 @@ -{ - "directory": "bower_components", - "analytics": false -} diff --git a/.ember-cli b/.ember-cli deleted file mode 100644 index ee64cfed2..000000000 --- a/.ember-cli +++ /dev/null @@ -1,9 +0,0 @@ -{ - /** - Ember CLI sends analytics information by default. The data is completely - anonymous, but there are times when you might want to disable this behavior. - - Setting `disableAnalytics` to true will prevent any data from being sent. - */ - "disableAnalytics": false -} diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index c8060733e..000000000 --- a/.eslintignore +++ /dev/null @@ -1,11 +0,0 @@ -# unconventional js -/blueprints/*/files/ - -# compiled output -/declarations/ -/dist/ - -# misc -/coverage/ -!.* -.*/ diff --git a/.eslintrc.js b/.eslintrc.js deleted file mode 100644 index 0f51e21cb..000000000 --- a/.eslintrc.js +++ /dev/null @@ -1,88 +0,0 @@ -module.exports = { - root: true, - parser: '@babel/eslint-parser', - parserOptions: { - ecmaVersion: 'latest', - sourceType: 'module', - requireConfigFile: false, - babelOptions: { - plugins: [ - [ - '@babel/plugin-proposal-decorators', - { - decoratorsBeforeExport: true, - }, - ], - ], - }, - }, - plugins: ['ember'], - extends: [ - 'eslint:recommended', - 'plugin:ember/recommended', - 'plugin:prettier/recommended', - '@addepar', - '@addepar/eslint-config/ember', - ], - env: { - browser: true, - }, - rules: { - 'ember/no-classic-classes': 'off', // 41 errors - 'ember/no-classic-components': 'off', // 20 errors - 'ember/no-component-lifecycle-hooks': 'off', // 12 errors - 'ember/no-get': 'off', // 406 errors - 'ember/no-runloop': 'off', // 26 errors - 'ember/require-computed-property-dependencies': 'off', // 23 errors - 'ember/require-tagless-components': 'off', // 14 errors - 'ember-best-practices/require-dependent-keys': 'off', // 56 errors - 'no-restricted-imports': [ - 'error', - { - paths: [ - { - name: '@ember/object', - importNames: ['observer'], - message: 'For compatibility, use `import { observer } from "-private/utils/observer"`', - }, - { - name: '@ember/object/observers', - importNames: ['addObserver', 'removeObserver'], - message: - 'For compatibility, use `import { addObserver, removeObserver } from "-private/utils/observer"`', - }, - ], - }, - ], - }, - overrides: [ - // node files - { - files: [ - './.eslintrc.js', - './.prettierrc.js', - './.stylelintrc.js', - './.template-lintrc.js', - './ember-cli-build.js', - './index.js', - './testem.js', - './blueprints/*/index.js', - './config/**/*.js', - './tests/dummy/config/**/*.js', - ], - parserOptions: { - sourceType: 'script', - }, - env: { - browser: false, - node: true, - }, - extends: ['plugin:n/recommended'], - }, - { - // test files - files: ['tests/**/*-test.{js,ts}'], - extends: ['plugin:qunit/recommended'], - }, - ], -}; diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ad8dd8bab..67d322bd3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,108 +3,90 @@ on: pull_request: types: [opened, synchronize, reopened, ready_for_review] +concurrency: + group: ci-${{ github.head_ref || github.ref }} + cancel-in-progress: true + jobs: - test: - name: Run Tests + lint: + name: Lint runs-on: ubuntu-latest - timeout-minutes: 7 + timeout-minutes: 10 steps: - name: "Install StepSecurity Action" - id: step_security uses: step-security/harden-runner@8d3c67de8e2fe68ef647c8db1e6a09f647780f40 # v2.19.0 with: egress-policy: audit - - - name: Checkout - uses: actions/checkout@v6 - - - name: Install mise and tools - uses: jdx/mise-action@c2a87611a18de5b3828c5652fe268e992400cb5c + - uses: actions/checkout@v6 + - uses: jdx/mise-action@c2a87611a18de5b3828c5652fe268e992400cb5c with: install: true cache: true - - name: Enable corepack - run: corepack enable - - - name: Node Modules Cache - id: cache-npm - uses: actions/cache@v5 - with: - path: '**/node_modules' - key: ci-yarn-${{ hashFiles('**/yarn.lock') }} - + - uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v4.4.0 - name: Install Dependencies - if: steps.cache-npm.outputs.cache-hit != 'true' - run: yarn install --frozen-lockfile - + run: pnpm install --frozen-lockfile - name: Lint - run: yarn lint + run: pnpm lint + + test: + name: Tests + runs-on: ubuntu-latest + timeout-minutes: 15 + outputs: + matrix: ${{ steps.set-matrix.outputs.matrix }} + steps: + - name: "Install StepSecurity Action" + uses: step-security/harden-runner@8d3c67de8e2fe68ef647c8db1e6a09f647780f40 # v2.19.0 + with: + egress-policy: audit + - uses: actions/checkout@v6 + - uses: jdx/mise-action@c2a87611a18de5b3828c5652fe268e992400cb5c + with: + install: true + cache: true + - uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v4.4.0 + - name: Install Dependencies + run: pnpm install --frozen-lockfile - name: Run Tests - run: yarn test + run: pnpm test env: CI: true + - id: set-matrix + working-directory: test-app + run: echo "matrix=$(pnpm -s dlx @embroider/try list)" >> $GITHUB_OUTPUT - test-ember-try: - name: Run Tests + try-scenarios: + name: ${{ matrix.name }} runs-on: ubuntu-latest - timeout-minutes: 10 - + needs: test + timeout-minutes: 15 strategy: fail-fast: false - matrix: - ember-version: - [ - embroider-safe, - # embroider-optimized, - ember-lts-3.28, - ember-lts-4.4, - ember-lts-4.12, - ember-lts-5.4, - ember-lts-5.12, - ember-release, - # ember-production, - ember-default-docs - ] + matrix: ${{ fromJson(needs.test.outputs.matrix) }} steps: - name: "Install StepSecurity Action" - id: step_security uses: step-security/harden-runner@8d3c67de8e2fe68ef647c8db1e6a09f647780f40 # v2.19.0 with: egress-policy: audit - - - name: Checkout - uses: actions/checkout@v6 - - - name: Install mise and tools - uses: jdx/mise-action@c2a87611a18de5b3828c5652fe268e992400cb5c + - uses: actions/checkout@v6 + - uses: jdx/mise-action@c2a87611a18de5b3828c5652fe268e992400cb5c with: install: true cache: true - - name: Enable corepack - run: corepack enable - - - name: Stash yarn.lock for cache key - run: cp yarn.lock __cache-key - - - name: Node Modules Cache - id: cache-npm - uses: actions/cache@v5 - with: - path: | - node_modules - package.json - yarn.lock - __env - key: ci-yarn-v3-${{ matrix.ember-version }}-${{ hashFiles('tests/dummy/config/ember-try.js', '__cache-key') }} - restore-keys: | - ci-yarn-${{ hashFiles('yarn.lock') }} - + - uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v4.4.0 + # Applied to both packages so the addon's dev copy of ember-source matches + # the app under test; otherwise the addon's modules resolve a second Ember. + - name: Apply Scenario + run: | + (cd test-app && pnpm dlx @embroider/try apply ${{ matrix.name }}) + (cd ember-table && pnpm dlx @embroider/try apply ${{ matrix.name }}) - name: Install Dependencies - if: steps.cache-npm.outputs.cache-hit != 'true' - run: yarn install --frozen-lockfile - + run: pnpm install --no-frozen-lockfile - name: Run Tests - run: yarn test:ember-compatibility ${{ matrix.ember-version }} --skip-cleanup + working-directory: test-app + run: pnpm test + env: + CI: true diff --git a/.gitignore b/.gitignore index 7ca59e8ca..ed506ed11 100644 --- a/.gitignore +++ b/.gitignore @@ -1,16 +1,16 @@ # See https://help.github.com/ignore-files/ for more about ignoring files. # compiled output -/dist -/tmp +dist/ +tmp/ # dependencies -/node_modules +node_modules/ /bower_components # misc /.DS_Store -/.eslintcache +.eslintcache /.sass-cache /connect.lock /coverage/* @@ -18,3 +18,7 @@ npm-debug.log* yarn-error.log testem.log + +# pnpm content store (created next to the repo when the global store is on another filesystem) +.pnpm-store/ +.vercel diff --git a/.ls-lint.yml b/.ls-lint.yml deleted file mode 100644 index 5f516db06..000000000 --- a/.ls-lint.yml +++ /dev/null @@ -1,20 +0,0 @@ -# Docs on ls-lint: https://ls-lint.org/ -# The linter can be run locally by `npx ls-lint [files]` -# Pass `--debug` flag to see the list of all files/dirs that are linted -ls: - .js: kebab-case - .d.ts: kebab-case - .ts: kebab-case - .html: kebab-case - .md: kebab-case | screamingsnakecase - .css: kebab-case - .scss: kebab-case - .dir: kebab-case - -ignore: - - .git - - node_modules - - dist - - ./.* - - yarn.lock - - yarn-error.log diff --git a/.npmignore b/.npmignore deleted file mode 100644 index 7e6e51a54..000000000 --- a/.npmignore +++ /dev/null @@ -1,16 +0,0 @@ -/bower_components -/dist -/tests -/tmp -**/.gitkeep -.bowerrc -.editorconfig -.ember-cli -.eslintcache -.eslintrc.js -.gitignore -.watchmanconfig -.travis.yml -bower.json -ember-cli-build.js -testem.js diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 000000000..4ee68fb64 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,6 @@ +dist/ +node_modules/ +coverage/ +tmp/ +pnpm-lock.yaml +CHANGELOG.md diff --git a/.prettierrc.cjs b/.prettierrc.cjs new file mode 100644 index 000000000..e6e993159 --- /dev/null +++ b/.prettierrc.cjs @@ -0,0 +1,5 @@ +// eslint-disable-next-line n/no-unpublished-require +module.exports = { + plugins: ['prettier-plugin-ember-template-tag'], + singleQuote: true, +}; diff --git a/.prettierrc.js b/.prettierrc.js deleted file mode 100644 index 9dbc32966..000000000 --- a/.prettierrc.js +++ /dev/null @@ -1,2 +0,0 @@ -// eslint-disable-next-line n/no-unpublished-require -module.exports = require('@addepar/prettier-config'); diff --git a/.sass-lint.yml b/.sass-lint.yml deleted file mode 100644 index f02b0f5c0..000000000 --- a/.sass-lint.yml +++ /dev/null @@ -1,8 +0,0 @@ -options: - config-file: node_modules/@addepar/sass-lint-config/config.yml -rules: - # Name Formats - class-name-format: - - 2 - - - convention: strictbem diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 000000000..9417fcf4c --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,7 @@ +{ + "recommendations": [ + // Types, completions and go-to-definition in `.gts`/`.gjs` templates. + "typed-ember.glint2-vscode", + "dbaeumer.vscode-eslint" + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json index 4718ffd99..e6e61f31c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,7 @@ { - "eslint.enable": true -} \ No newline at end of file + "eslint.enable": true, + // TypeScript server plugins (Glint's, for `.gts`) load only in the JS-based + // TypeScript server, so use the workspace's TypeScript. + "typescript.tsdk": "node_modules/typescript/lib", + "typescript.enablePromptUseWorkspaceTsdk": true +} diff --git a/.watchmanconfig b/.watchmanconfig deleted file mode 100644 index e7834e3e4..000000000 --- a/.watchmanconfig +++ /dev/null @@ -1,3 +0,0 @@ -{ - "ignore_dirs": ["tmp", "dist"] -} diff --git a/.yarnrc b/.yarnrc deleted file mode 100644 index 45291c133..000000000 --- a/.yarnrc +++ /dev/null @@ -1 +0,0 @@ -registry "https://registry.npmjs.org/" diff --git a/README.md b/README.md index 0a0fbf124..9250182a2 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ Ember Table versions each support a range of browsers and framework versions: | Ember Table Version | Ember Versions Supported | Browser Support | | ------------------- | ---------------------------- | --------------- | -| 6.x (prerelease) | 3.28 - 6.x | Last two versions of Chrome, Safari, Edge, Firefox on desktop and mobile. | +| 6.x (prerelease) | 5.12 - 7.x | Last two versions of Chrome, Safari, Edge, Firefox on desktop and mobile. | | 5.x | 3.12 - 4.x (possibly 5.x?) | Last two versions of Chrome, Safari, Edge, Firefox on desktop and mobile. | | 4.x | 2.18 - 4.x | Last two versions of Chrome, Safari, Edge, Firefox on desktop and mobile. | | 3.x | 2.8 - 3.28 (last 3.x version | Last two versions of Chrome, Safari, Edge, Firefox on desktop and mobile. | @@ -18,9 +18,19 @@ Ember Table versions each support a range of browsers and framework versions: ## Install ```bash -ember install ember-table +pnpm add ember-table ``` +Ember Table is a [v2 addon](https://rfcs.emberjs.com/id/0507-embroider-v2-package-format/). +It works in Vite apps and, through `ember-auto-import`, in classic ember-cli apps. In `.gjs`/`.gts` +templates, import the components from the package: + +```gjs +import { EmberTable, EmberThead, EmberTbody } from 'ember-table'; +``` + +Classic apps can also invoke them by name (``) from loose-mode templates. + ## Features - Column resizing, column reordering. @@ -35,8 +45,14 @@ ember install ember-table Documentation is available at: https://opensource.addepar.com/ember-table/docs -Ember Table uses [ember-cli-addon-docs](https://github.com/ember-learn/ember-cli-addon-docs) for its documentation. -To run the docs locally, clone the repo, run `yarn && yarn start` and then navigate to `http://localhost:4200/docs`. +The documentation site deploys to [Vercel](https://vercel.com) from the repository root +(`vercel.json`): it installs the workspace, builds the addon, and serves the test app's +production build. It is built with [Docfy](https://docfy.dev) from the Markdown in +[`test-app/docs`](./test-app/docs). Every example is a live `gjs` component whose source is shown +alongside it, highlighted with Shiki's Glimmer grammars. The API reference pages are generated +from the addon's type declarations (`test-app/scripts/api-docs.mjs`), so argument tables, defaults +and descriptions come from the doc comments in `ember-table/src`. To run the docs locally, clone the repo, run `pnpm install && pnpm start`, and open +the URL Vite prints (`/docs`). ## Usage @@ -126,7 +142,14 @@ you like): ``` The rendered table is a plain table without any styling. You can define styling for your own table. -If you want to use default table style, import the `ember-table/default` SASS file. +If you want to use the default table style, import the `ember-table/default.scss` Sass file: + +```scss +@use 'ember-table/default.scss'; +``` + +Vite resolves this through the package's `exports`. With the classic `ember-cli-sass` +pipeline, add `node_modules` to its `includePaths` and import `ember-table/dist/styles/default`. ### Optional Footer @@ -155,62 +178,89 @@ import { setupForTest as setupEmberTableForTest } from 'ember-table/test-support setupEmberTableForTest(); ``` -## EXPERIMENTAL: Using Ember Table with Glint +### Stripping test selectors from production builds -Ember Table provides **experimental** Glint types defined in the `/types/` directory. -These types may change at any time and are **NOT** covered by Ember Table's semantic versioning. -They are intended to support standard documented usage of Ember Table and do not attempt to type the internals of the Ember Table addon. -If you are using Ember Table in a more advanced way (such as extending Ember Table components), you will still need to define your own types for those use cases. +Ember Table's templates carry `data-test-*` attributes for its page objects. They are compiled by +your app, so your app decides whether to strip them. In a Vite app, add +[`strip-test-selectors`](https://github.com/mainmatter/ember-test-selectors/tree/master/strip-test-selectors) +to the template transforms for production builds; it strips your own templates' selectors too: -### Glint Types Installation +```js +// babel.config.mjs +export default function (api) { + return { + plugins: [ + [ + 'babel-plugin-ember-template-compilation', + { + transforms: [ + ...macros.templateMacros, + ...(api.env('production') ? ['strip-test-selectors'] : []), + ], + }, + ], + // ... + ], + }; +} +``` -Assuming you have the Ember Table addon installed, you can import and register Ember Table's Glint types in the manner [recommended by the Glint docs](https://typed-ember.gitbook.io/glint/using-glint/ember/using-addons#using-glint-enabled-addons): +Build your tests in a non-production mode (e.g. `vite build --mode development`) so the +selectors stay. -```ts -// types/global.d.ts -import '@glint/environment-ember-loose'; -import EmberTableRegistry from 'ember-table/template-registry'; +## Using Ember Table with TypeScript and Glint -declare module '@glint/environment-ember-loose/registry' { - export default interface Registry extends EmberTableRegistry, /* other addon registries */ { - // local entries - } -} -``` +Ember Table is written in TypeScript, and its type declarations are generated from the +source. Components, their signatures (`EmberTableSignature`, `EmberTheadArgs`, +`EmberTbodyArgs`, ...) and the shared interfaces (`EmberTableColumn`, `EmberTableRow`, +`EmberTableSort`, `TableColumnMeta`, `TableRowMeta`) are importable from the package. -### Glint Types Usage +### Template-tag components (`.gts`) -1. Define a type interface for your row contents. If your columns contain additional custom attributes, you can type those as well. Ember Table provides default interfaces that can be extended for this purpose. -1. Extend the base Ember Table component passing in your row and (optional) column interfaces as generics. -1. Use this extended version of the Ember Table component in your template. +Import the components and type your rows by specializing `EmberTable` with an +instantiation expression. Rows passed to the body and values yielded by rows are then +checked against your interface: -```ts -// my-table-component.ts +```gts +import EmberTable from 'ember-table/components/ember-table/component'; import type { EmberTableColumn, EmberTableRow } from 'ember-table'; -import EmberTableComponent from 'ember-table/components/ember-table/component'; -interface MyTableColumn extends EmberTableColumn { - // Add any custom column attribute types here (optional) +interface Person extends EmberTableRow { + firstName: string; + age: number; } -interface MyTableRow extends EmberTableRow { - // Add the attributes and types for your table rows here -} +const PeopleTable = EmberTable; -class MyEmberTableComponent extends EmberTableComponent {} +const columns: EmberTableColumn[] = [{ name: 'First Name', valuePath: 'firstName' }]; -export default class MyTableComponent extends Component { - emberTableComponent = MyEmberTableComponent; -} + ``` -```hbs -{{! my-table-component.hbs }} - - {{! Use Ember Table as usual. Row and column arguments will be enforced to match the appropriate types. }} - {{! Yielded items (rows, columns) will be typed according to the specified interfaces. }} - {{! Cell values will be typed as a union of all defined row attribute types. }} - +Without a row type, yielded cell values are untyped. + +### Loose-mode templates (`.hbs`) + +Apps type-checking loose-mode templates can merge Ember Table's template registry into +their own: + +```ts +// types/global.d.ts +import '@glint/environment-ember-loose'; +import type EmberTableRegistry from 'ember-table/template-registry'; + +declare module '@glint/environment-ember-loose/registry' { + export default interface Registry extends EmberTableRegistry /* other addon registries */ {} +} ``` ## Migrating from old Ember table @@ -234,30 +284,34 @@ one table at at time. The recommended migration steps are as follows (if you are ### Releasing new versions (for maintainers) -We use [`release-it`](https://github.com/release-it/release-it). -To create a new release, run `yarn run release`. To do a dry-run: `yarn run release --dry-run`. +We use [`release-it`](https://github.com/release-it/release-it) from the `ember-table` package +directory. To create a new release, run `pnpm release` there. To do a dry-run: `pnpm release --dry-run`. The tool will prompt you to select the new release version. **You must be a member of the @Addepar/web-core team on GitHub to bypass master branch protection.** -### Generating documentation. +### Development -This library is documented using Ember Addon Docs. v0.6.3+ of that library -bring a CSS reset files into the test suite of Ember Table, meaning many -tests would be corrupted away from the useragent styles they were written -against. +This repository is a pnpm workspace: -Because of this, building the docs requires going through Ember Try. For -example to run tests asserting the docs build: +- `ember-table/` is the published v2 addon (`pnpm --filter ember-table build`). +- `test-app/` is a Vite app that hosts both the test suite and the documentation site. -``` -ember try:one ember-default-docs -``` +In VS Code, install the recommended extensions (`.vscode/extensions.json`), notably +[Glint](https://marketplace.visualstudio.com/items?itemName=typed-ember.glint2-vscode), for types and +go-to-definition in `.gts` files, and use the workspace TypeScript version when prompted. Run +`pnpm build` once so the test app's project reference can follow imports into the addon's source. -You might also want to run a command with the addon docs libraries installed, -for example to create a production build, and you can do so like this: +Run `pnpm test` from the root to build the addon and run the test suite. Where no local Chrome is +available, point Testem at a containerized Chromium: +`CHROME_BIN=$PWD/test-app/scripts/docker-chrome pnpm test`. -``` -ember try:one ember-default-docs --- ember build -e production +Compatibility scenarios use [`@embroider/try`](https://github.com/embroider-build/embroider/tree/main/packages/try). +Apply a scenario to both packages (see `.github/workflows/ci.yml`), reinstall, and test: + +```bash +(cd test-app && pnpm dlx @embroider/try apply ember-lts-6.4) +(cd ember-table && pnpm dlx @embroider/try apply ember-lts-6.4) +pnpm install --no-frozen-lockfile && pnpm test ``` diff --git a/SPIKE.md b/SPIKE.md new file mode 100644 index 000000000..f3daf02b9 --- /dev/null +++ b/SPIKE.md @@ -0,0 +1,65 @@ +# Ember 7 / v2 addon modernization spike + +This branch moves `ember-table` from its Ember 3.28-era v1 addon to a v2 addon +written in TypeScript, tested and documented in a Vite app. + +## Layout + +- `ember-table/`: the published v2 addon, entirely TypeScript. Components are + `.gts` with Glint signatures; declarations are generated from source. The + `ColumnTree`/`CollapseTree` models are still classic `EmberObject`s + (computed properties, observers), written as native classes; components see + them through the interfaces in `src/-private/types.ts`. +- `test-app/`: a Vite app hosting the test suite and the Docfy documentation + site (`test-app/docs`). It has no ember-cli or compat layer: just the + `ember()` Vite plugin and `ember-strict-application-resolver`, with every + module it resolves listed in `app/app.ts`. Tests use strict-mode + `