@operato/data-grist 10.29.14 → 10.29.18

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.14",
3
+ "version": "10.29.18",
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.14",
73
+ "@operato/input": "^10.29.17",
74
74
  "@operato/list-state": "^10.29.1",
75
- "@operato/p13n": "^10.29.14",
76
- "@operato/popup": "^10.29.14",
75
+ "@operato/p13n": "^10.29.17",
76
+ "@operato/popup": "^10.29.17",
77
77
  "@operato/pull-to-refresh": "^10.29.1",
78
- "@operato/styles": "^10.29.12",
78
+ "@operato/styles": "^10.29.17",
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": "44bef05a4a98b16055bbe20e2493e8cdca28de96"
119
+ "gitHead": "f3adef5197cdb03b1efbdb123dc935a80b6ddc00"
120
120
  }
@@ -0,0 +1,68 @@
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 { html } from 'lit'
4
+
5
+ import '../src/data-grist.js'
6
+ import '../src/filters/filters-form.js'
7
+
8
+ /*
9
+ * Every filter in the form says what it filters, beside its chooser. A select once carried its name only as a
10
+ * placeholder, which a chosen value replaces - the bar then showed 「진행 중」 with nothing saying it was the status.
11
+ */
12
+
13
+ const columns = [
14
+ { type: 'string', name: 'name', header: 'name', filter: 'i_like' },
15
+ { type: 'select', name: 'status', header: 'status', record: { options: ['', 'OPEN', 'CLOSED'] }, filter: 'in' },
16
+ { type: 'select', name: 'kind', header: 'kind', record: { options: ['', 'A', 'B'] }, filter: true },
17
+ { type: 'date', name: 'day', header: 'day', filter: 'between' }
18
+ ]
19
+
20
+ const settle = (ms = 150) => new Promise(resolve => setTimeout(resolve, ms))
21
+
22
+ describe('필터 라벨', () => {
23
+ const ignoreResizeLoop = (e: ErrorEvent) => {
24
+ if (String(e.message).includes('ResizeObserver loop')) {
25
+ e.stopImmediatePropagation()
26
+ e.preventDefault()
27
+ }
28
+ }
29
+ let previousOnError: OnErrorEventHandler = null
30
+
31
+ before(() => {
32
+ window.addEventListener('error', ignoreResizeLoop, true)
33
+ previousOnError = window.onerror
34
+ window.onerror = (message, ...rest) =>
35
+ String(message).includes('ResizeObserver loop') ? true : previousOnError?.call(window, message, ...rest)
36
+ })
37
+
38
+ after(() => {
39
+ window.removeEventListener('error', ignoreResizeLoop, true)
40
+ window.onerror = previousOnError
41
+ })
42
+
43
+ it('every filter, a select too, sits in a label that names it', async () => {
44
+ const el = (await fixture(html`
45
+ <ox-grist
46
+ style="height: 300px"
47
+ mode="GRID"
48
+ .config=${{ columns, pagination: { pages: [20] } }}
49
+ .fetchHandler=${async () => ({ page: 1, limit: 20, total: 0, records: [] })}
50
+ >
51
+ <div slot="headroom"><ox-filters-form without-search></ox-filters-form></div>
52
+ </ox-grist>
53
+ `)) as any
54
+ await settle()
55
+
56
+ const form = el.querySelector('ox-filters-form') as any
57
+ await form.updateComplete
58
+ const root = form.renderRoot as ShadowRoot
59
+ const named = (name: string) => root.querySelector(`[name="${name}"]`)
60
+ for (const name of ['name', 'status', 'kind', 'day']) {
61
+ const chooser = named(name)
62
+ expect(!!chooser, `${name} is rendered`).to.equal(true)
63
+ const label = chooser!.closest('label[filter-title]')
64
+ expect(!!label, `${name} sits in a label`).to.equal(true)
65
+ expect(label!.querySelector('span')?.textContent?.trim()).to.equal(name)
66
+ }
67
+ })
68
+ })
@@ -0,0 +1,68 @@
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 { html } from 'lit'
4
+
5
+ import '../src/record-view/record-view.js'
6
+ import { buildColumn } from '../src/configure/column-builder.js'
7
+ import { OxGristEditor } from '../src/editors/ox-grist-editor.js'
8
+ import { registerEditor } from '../src/editors/registry.js'
9
+
10
+ /*
11
+ * In a form (ox-record-view - the 「만들기」 dialog of a grid) a field is pressed to be edited. A field whose editor
12
+ * opens a popup used to become 「editing」 on the first press but open nothing until its editor was pressed again,
13
+ * so what was typed after the first press went nowhere (pdf template 「만들기」, body HTML, 2026-10-10).
14
+ */
15
+
16
+ let opened = 0
17
+
18
+ class ProbePopupEditor extends OxGristEditor {
19
+ static opensPopup = true
20
+
21
+ _onclick(e: Event): void {
22
+ e.stopPropagation()
23
+ opened++
24
+ }
25
+ }
26
+ customElements.define('probe-popup-editor', ProbePopupEditor)
27
+ registerEditor('probe-popup', ProbePopupEditor)
28
+
29
+ const settle = (ms = 100) => new Promise(resolve => setTimeout(resolve, ms))
30
+
31
+ describe('a popup field in a form', () => {
32
+ it('opens its popup on the first press', async () => {
33
+ opened = 0
34
+ const columns = [
35
+ buildColumn({ type: 'string', name: 'name', header: 'name', record: { editable: true } } as any),
36
+ buildColumn({ type: 'probe-popup', name: 'body', header: 'body', record: { editable: true } } as any)
37
+ ]
38
+ const view = (await fixture(html`<ox-record-view .columns=${columns} .record=${{}}></ox-record-view>`)) as any
39
+ await view.updateComplete
40
+ await settle()
41
+
42
+ const body = view.renderRoot.querySelector('ox-record-view-body') as any
43
+ await body.updateComplete
44
+ const field = Array.from(body.renderRoot.querySelectorAll('ox-grid-field') as NodeListOf<any>).find(one => one.column?.name === 'body')
45
+ expect(!!field, 'the body field is drawn').to.equal(true)
46
+
47
+ field.click()
48
+ await settle(200)
49
+
50
+ expect(field.getAttribute('editing'), 'the field is being edited').to.equal('true')
51
+ expect(opened, 'the popup editor was opened once').to.equal(1)
52
+ })
53
+
54
+ it('leaves a field edited in place as it was - nothing extra is pressed', async () => {
55
+ opened = 0
56
+ const columns = [buildColumn({ type: 'string', name: 'name', header: 'name', record: { editable: true } } as any)]
57
+ const view = (await fixture(html`<ox-record-view .columns=${columns} .record=${{}}></ox-record-view>`)) as any
58
+ await view.updateComplete
59
+ await settle()
60
+ const body = view.renderRoot.querySelector('ox-record-view-body') as any
61
+ await body.updateComplete
62
+ const field = body.renderRoot.querySelector('ox-grid-field') as any
63
+ field.click()
64
+ await settle(200)
65
+ expect(field.getAttribute('editing')).to.equal('true')
66
+ expect(opened).to.equal(0)
67
+ })
68
+ })