@operato/data-grist 10.29.7 → 10.29.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@operato/data-grist",
3
- "version": "10.29.7",
3
+ "version": "10.29.9",
4
4
  "description": "User interface for grid (desktop) and list (mobile)",
5
5
  "author": "heartyoh",
6
6
  "type": "module",
@@ -70,12 +70,12 @@
70
70
  "@material/web": "^2.0.0",
71
71
  "@operato/headroom": "^10.29.1",
72
72
  "@operato/i18n": "^10.29.1",
73
- "@operato/input": "^10.29.1",
73
+ "@operato/input": "^10.29.9",
74
74
  "@operato/list-state": "^10.29.1",
75
- "@operato/p13n": "^10.29.5",
76
- "@operato/popup": "^10.29.1",
75
+ "@operato/p13n": "^10.29.9",
76
+ "@operato/popup": "^10.29.9",
77
77
  "@operato/pull-to-refresh": "^10.29.1",
78
- "@operato/styles": "^10.29.1",
78
+ "@operato/styles": "^10.29.9",
79
79
  "@operato/time-calculator": "^10.29.1",
80
80
  "@operato/utils": "^10.29.1",
81
81
  "i18next": "^24.2.1",
@@ -116,5 +116,5 @@
116
116
  "prettier --write"
117
117
  ]
118
118
  },
119
- "gitHead": "9e14db8a889f2b59cc0ad9018861b14cf9490ce1"
119
+ "gitHead": "24e4809ab0168eef48792291f72b11f75d8179ad"
120
120
  }
@@ -0,0 +1,101 @@
1
+ /* Elements are compared by presence, not by value: a failing element comparison makes chai render the DOM and hang the runner. */
2
+ import { expect, fixture } from '@open-wc/testing'
3
+ import { resetMouse, sendMouse } from '@web/test-runner-commands'
4
+ import { html } from 'lit'
5
+
6
+ import '../src/data-grist.js'
7
+
8
+ /**
9
+ * The first cell you click stays where you clicked it.
10
+ *
11
+ * Clicking a cell sets the new focus and calls focus() at once, before the body has drawn that
12
+ * focus. The input buffer that takes the focus was still where it had never been placed (top left),
13
+ * and the browser scrolled the grid to show it. Then the grid scrolled the focused cell back into
14
+ * view — at the far edge. A cell clicked in the middle of a grid pushed 1000px to the right ended up
15
+ * at the right edge (0, then 581). From the second click on the buffer sat near the last cell, so it
16
+ * only showed the first time.
17
+ *
18
+ * The grid scrolls its own focus into view, so focusing the buffer must not scroll.
19
+ */
20
+
21
+ type Root = HTMLElement & { renderRoot: ShadowRoot; updateComplete: Promise<unknown> }
22
+
23
+ const settle = (ms = 150) => new Promise(resolve => setTimeout(resolve, ms))
24
+
25
+ describe('the first click on a scrolled grid', () => {
26
+ const ignoreResizeLoop = (e: ErrorEvent) => {
27
+ if (String(e.message).includes('ResizeObserver loop')) {
28
+ e.stopImmediatePropagation()
29
+ e.preventDefault()
30
+ }
31
+ }
32
+ let previousOnError: OnErrorEventHandler = null
33
+ before(() => {
34
+ window.addEventListener('error', ignoreResizeLoop, true)
35
+ previousOnError = window.onerror
36
+ window.onerror = (message, ...rest) =>
37
+ String(message).includes('ResizeObserver loop') ? true : previousOnError?.call(window, message, ...rest)
38
+ })
39
+ after(async function () {
40
+ /* resetMouse is a round trip to the browser driver; under a full parallel run it takes seconds. */
41
+ this.timeout(10000)
42
+ /* Let the resize that the last layout queued arrive while the filter is still in place. */
43
+ await settle(300)
44
+ window.removeEventListener('error', ignoreResizeLoop, true)
45
+ window.onerror = previousOnError
46
+ await resetMouse()
47
+ })
48
+
49
+ it('does not move the grid when the clicked cell is already in view', async function () {
50
+ this.timeout(10000)
51
+ const columns: any[] = [
52
+ { type: 'gutter', gutterName: 'sequence' },
53
+ { type: 'string', name: 'id', hidden: true },
54
+ { type: 'string', name: 'code', header: 'code', width: 140, record: { editable: true } }
55
+ ]
56
+ for (let i = 0; i < 30; i++) {
57
+ columns.push({ type: 'string', name: 'c' + i, header: 'col ' + i, width: 120, record: { editable: true } })
58
+ }
59
+ const records = Array.from({ length: 30 }, (_, r) => {
60
+ const record: any = { id: String(r), code: 'C-' + r }
61
+ for (let i = 0; i < 30; i++) record['c' + i] = `v${r}-${i}`
62
+ return record
63
+ })
64
+
65
+ const el = (await fixture(html`
66
+ <ox-grist
67
+ style="display: flex; width: 700px; height: 360px"
68
+ mode="GRID"
69
+ .config=${{ columns, pagination: { pages: [100] } }}
70
+ .fetchHandler=${async () => ({ total: records.length, records })}
71
+ ></ox-grist>
72
+ `)) as Root & { fetch(): Promise<void> }
73
+ await el.fetch()
74
+ await settle()
75
+
76
+ const grist = el.renderRoot.querySelector('#grist') as Root
77
+ const scroller = grist.renderRoot.querySelector('div[grid]') as HTMLElement
78
+ const body = grist.renderRoot.querySelector('ox-grid-body') as Root
79
+ await body.updateComplete
80
+
81
+ scroller.scrollLeft = 1000
82
+ await settle(50)
83
+ expect(scroller.scrollLeft, 'the grid must scroll, or this measures nothing').to.equal(1000)
84
+
85
+ /* A cell in the middle of the view, in the third row. */
86
+ const row = body.renderRoot.querySelectorAll('div[grid-row]')[2] as HTMLElement
87
+ const view = scroller.getBoundingClientRect()
88
+ const cell = (Array.from(row.querySelectorAll('ox-grid-field')) as HTMLElement[]).find(f => {
89
+ const r = f.getBoundingClientRect()
90
+ return r.left > view.left + 200 && r.right < view.right - 200
91
+ })!
92
+ const before = cell.getBoundingClientRect()
93
+
94
+ await sendMouse({ type: 'click', position: [Math.round(before.left + 20), Math.round(before.top + before.height / 2)] })
95
+ await settle()
96
+
97
+ expect(cell.hasAttribute('focused'), 'the clicked cell takes the focus').to.equal(true)
98
+ expect(scroller.scrollLeft, 'the grid moved under the pointer').to.equal(1000)
99
+ expect(Math.round(cell.getBoundingClientRect().left)).to.equal(Math.round(before.left))
100
+ })
101
+ })