ngx-datatable
ngx-datatable copied to clipboard
feat: add touch support for reorderable and resizable features
- Enabled touch event handling in key directives such as draggable, resizeable, and long-press, ensuring compatibility with touch devices.
- Introduced utility functions for identifying touch events and extracting event coordinates.
- Updated styles to enhance usability on touch devices.
What kind of change does this PR introduce? (check one with "x")
- [ ] Bugfix
- [x] Feature
- [ ] Code style update (formatting, local variables)
- [ ] Refactoring (no functional changes, no api changes)
- [ ] Build related changes
- [ ] CI related changes
- [ ] Other... Please describe:
What is the current behavior? (You can also link to an open issue here) Columns can't be resized or reordered.
What is the new behavior? Columns can be resized and reordered.
Does this PR introduce a breaking change? (check one with "x")
- [ ] Yes
- [x] No
If this PR contains a breaking change, please describe the impact and migration path for existing applications: ...
Other information:
Sure I'll look into Playwright some time this week.
I did find an open issue talking about support for Touch Events: https://github.com/microsoft/playwright/issues/2903 But I'll investigate further if there is a workaround.
I think in that case you can also skip adding playwright tests. Since we anyway have to construct the event manually unit tests will do. But there is now one existing test failing (e2e/sorting.spec.ts:85 ). This is the relevant error message:
Error: Timed out 5000ms waiting for expect(locator).toHaveClass(expected)
Locator: locator('datatable-header-cell[title="Company"]')
Expected pattern: /sort-desc/
Received string: "datatable-header-cell resizeable sortable sort-active sort-asc longpress"
Call log:
- expect.toHaveClass with timeout 5000ms
- waiting for locator('datatable-header-cell[title="Company"]')
- locator resolved to <datatable-header-cell draggable="" tabindex="0" resizeable="" long-press="" title="Company" role="columnheader" ng-reflect-drag-x="true" ng-reflect-drag-y="false" ng-reflect-sort-type="single" ng-reflect-press-enabled="true" ng-reflect-header-height="50px" ng-reflect-resize-enabled="true" ng-reflect-sorts="[object Object]" ng-reflect-column="[object Object]" ng-reflect-all-rows-selected="false" ng-reflect-drag-model="[object Object]" ng-reflect-press-model="[object Object]" ng-reflect-enable-clearing-sort-state="fa…>…</datatable-header-cell>
- unexpected value "datatable-header-cell resizeable sortable sort-active sort-asc"
- locator resolved to <datatable-header-cell draggable="" tabindex="0" resizeable="" long-press="" title="Company" role="columnheader" ng-reflect-drag-x="true" ng-reflect-drag-y="false" ng-reflect-sort-type="single" ng-reflect-press-enabled="true" ng-reflect-header-height="50px" ng-reflect-resize-enabled="true" ng-reflect-sorts="[object Object]" ng-reflect-column="[object Object]" ng-reflect-all-rows-selected="false" ng-reflect-drag-model="[object Object]" ng-reflect-press-model="[object Object]" ng-reflect-enable-clearing-sort-state="fa…>…</datatable-header-cell>
- unexpected value "datatable-header-cell resizeable sortable sort-active sort-asc"
- locator resolved to <datatable-header-cell draggable="" tabindex="0" resizeable="" long-press="" title="Company" role="columnheader" ng-reflect-drag-x="true" ng-reflect-drag-y="false" ng-reflect-sort-type="single" ng-reflect-press-enabled="true" ng-reflect-header-height="50px" ng-reflect-resize-enabled="true" ng-reflect-sorts="[object Object]" ng-reflect-column="[object Object]" ng-reflect-all-rows-selected="false" ng-reflect-drag-model="[object Object]" ng-reflect-press-model="[object Object]" ng-reflect-enable-clearing-sort-state="fa…>…</datatable-header-cell>
- unexpected value "datatable-header-cell resizeable sortable sort-active sort-asc"
- locator resolved to <datatable-header-cell draggable="" tabindex="0" resizeable="" long-press="" title="Company" role="columnheader" ng-reflect-drag-x="true" ng-reflect-drag-y="false" ng-reflect-sort-type="single" ng-reflect-press-enabled="true" ng-reflect-header-height="50px" ng-reflect-resize-enabled="true" ng-reflect-sorts="[object Object]" ng-reflect-column="[object Object]" ng-reflect-all-rows-selected="false" ng-reflect-drag-model="[object Object]" ng-reflect-press-model="[object Object]" ng-reflect-enable-clearing-sort-state="fa…>…</datatable-header-cell>
- unexpected value "datatable-header-cell resizeable sortable sort-active sort-asc"
- locator resolved to <datatable-header-cell draggable="" tabindex="0" resizeable="" long-press="" title="Company" role="columnheader" ng-reflect-drag-x="true" ng-reflect-drag-y="false" ng-reflect-sort-type="single" ng-reflect-press-enabled="true" ng-reflect-header-height="50px" ng-reflect-resize-enabled="true" ng-reflect-sorts="[object Object]" ng-reflect-column="[object Object]" ng-reflect-all-rows-selected="false" ng-reflect-drag-model="[object Object]" ng-reflect-press-model="[object Object]" ng-reflect-enable-clearing-sort-state="fa…>…</datatable-header-cell>
- unexpected value "datatable-header-cell resizeable sortable sort-active sort-asc longpress"
- locator resolved to <datatable-header-cell draggable="" tabindex="0" resizeable="" long-press="" title="Company" role="columnheader" ng-reflect-drag-x="true" ng-reflect-drag-y="false" ng-reflect-sort-type="single" ng-reflect-press-enabled="true" ng-reflect-header-height="50px" ng-reflect-resize-enabled="true" ng-reflect-sorts="[object Object]" ng-reflect-column="[object Object]" ng-reflect-all-rows-selected="false" ng-reflect-drag-model="[object Object]" ng-reflect-press-model="[object Object]" ng-reflect-enable-clearing-sort-state="fa…>…</datatable-header-cell>
- unexpected value "datatable-header-cell resizeable sortable sort-active sort-asc longpress"
- locator resolved to <datatable-header-cell draggable="" tabindex="0" resizeable="" long-press="" title="Company" role="columnheader" ng-reflect-drag-x="true" ng-reflect-drag-y="false" ng-reflect-sort-type="single" ng-reflect-press-enabled="true" ng-reflect-header-height="50px" ng-reflect-resize-enabled="true" ng-reflect-sorts="[object Object]" ng-reflect-column="[object Object]" ng-reflect-all-rows-selected="false" ng-reflect-drag-model="[object Object]" ng-reflect-press-model="[object Object]" ng-reflect-enable-clearing-sort-state="fa…>…</datatable-header-cell>
- unexpected value "datatable-header-cell resizeable sortable sort-active sort-asc longpress"
- locator resolved to <datatable-header-cell draggable="" tabindex="0" resizeable="" long-press="" title="Company" role="columnheader" ng-reflect-drag-x="true" ng-reflect-drag-y="false" ng-reflect-sort-type="single" ng-reflect-press-enabled="true" ng-reflect-header-height="50px" ng-reflect-resize-enabled="true" ng-reflect-sorts="[object Object]" ng-reflect-column="[object Object]" ng-reflect-all-rows-selected="false" ng-reflect-drag-model="[object Object]" ng-reflect-press-model="[object Object]" ng-reflect-enable-clearing-sort-state="fa…>…</datatable-header-cell>
- unexpected value "datatable-header-cell resizeable sortable sort-active sort-asc longpress"
- locator resolved to <datatable-header-cell draggable="" tabindex="0" resizeable="" long-press="" title="Company" role="columnheader" ng-reflect-drag-x="true" ng-reflect-drag-y="false" ng-reflect-sort-type="single" ng-reflect-press-enabled="true" ng-reflect-header-height="50px" ng-reflect-resize-enabled="true" ng-reflect-sorts="[object Object]" ng-reflect-column="[object Object]" ng-reflect-all-rows-selected="false" ng-reflect-drag-model="[object Object]" ng-reflect-press-model="[object Object]" ng-reflect-enable-clearing-sort-state="fa…>…</datatable-header-cell>
- unexpected value "datatable-header-cell resizeable sortable sort-active sort-asc longpress"
106 |
107 | await expect(companyHeader).toHaveClass(/sort-active/);
> 108 | await expect(companyHeader).toHaveClass(/sort-desc/);
| ^
109 |
110 | await expect(loadingIndicator).toBeVisible();
111 |
at /e2e/playwright/e2e/sorting.spec.ts:108:35
Just one more thing, my previous comment broke the code on Firefox. Seems like the desktop version does not support ToucheEvent, so please write the check like this: const isMouse = event instanceof MouseEvent;
I have updated the PR with the MouseEvent change, but sadly the latest changes completely broke the PR.
I'll have to look over, and re-implement some stuff.
And I still haven't found any option for playwright touch events.
I rebased the pull request.
I skipped Playwright and Unit test for now, if that's ok for you.
thx a lot. Your changes are looking good.
We are currently facing CI issues, most likely due to github changing terms of use without informing us and we are now running int git lfs quota issues. Once those are resolved I will merge your PR.
FYI, we are still having the LFS issues. Github support should be on it, but so far they were not able to fix anything.
@ColinFrick again, we're extremely sorry that this PR takes so long, we're still waiting for GitHub's support to resolve the Git LFS isssue for good. However, we were able to come up with a workaround so that the builds should work again as expected.
I sadly don't have the rights to rebase your PR or trigger another action run. Would you mind to quickly give it a rebase, so the pipeline passes? I'll then accept the PR ASAP. Many thanks in advance 🙇
@fh1ch Don't sweat it. I have merged the upstream into the PR, and fixed any merge conflicts.
Seems like there are still conflicts and also a format issue.
Ideally you do not merge master-origin into your branch but rebase onto our master. That should solve the issue.
@spike-rabbit Sorry about that. I have rebased the PR and run prettier.
@fh1ch Thanks for the merge!
OT Are you planning to migrate to signal inputs? And are you accepting PRs, respectively? The package would benefit from a centralized signal state management instead of prop drilling.
@ColinFrick yes, we are also planning to do this later this year. Right now the focus is on improving types and solving the most broken parts.. Once this is done, the next step would be indeed migrating to signals and clean-up the whole property passing mess. After that we will finally fix all the real issues, like responsive behavior and a11y.
I will message you directly so we can talk.