@operato/data-grist 10.10.0 → 10.10.2
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/CHANGELOG.md +18 -0
- package/coverage/lcov-report/base.css +224 -0
- package/coverage/lcov-report/block-navigation.js +87 -0
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +866 -0
- package/coverage/lcov-report/prettify.css +1 -0
- package/coverage/lcov-report/prettify.js +2 -0
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +210 -0
- package/coverage/lcov-report/webpack/bootstrap.html +118 -0
- package/coverage/lcov-report/webpack/index.html +116 -0
- package/coverage/lcov-report/webpack/runtime/define property getters.html +118 -0
- package/coverage/lcov-report/webpack/runtime/global.html +118 -0
- package/coverage/lcov-report/webpack/runtime/hasOwnProperty shorthand.html +118 -0
- package/coverage/lcov-report/webpack/runtime/index.html +161 -0
- package/coverage/lcov-report/webpack/runtime/make namespace object.html +118 -0
- package/coverage/lcov.info +53451 -0
- package/dist/src/configure/column-builder.js +6 -7
- package/dist/src/configure/column-builder.js.map +1 -1
- package/dist/src/data-grid/data-grid-body-style.js +4 -4
- package/dist/src/data-grid/data-grid-body-style.js.map +1 -1
- package/dist/src/data-grid/data-grid-body.d.ts +11 -11
- package/dist/src/data-grid/data-grid-body.js +33 -35
- package/dist/src/data-grid/data-grid-body.js.map +1 -1
- package/dist/src/data-grid/data-grid-field.js +13 -0
- package/dist/src/data-grid/data-grid-field.js.map +1 -1
- package/dist/src/data-grid/data-grid-header.d.ts +6 -6
- package/dist/src/data-grid/data-grid-header.js +22 -21
- package/dist/src/data-grid/data-grid-header.js.map +1 -1
- package/dist/src/data-grid/data-grid.d.ts +4 -4
- package/dist/src/data-grid/data-grid.js +24 -24
- package/dist/src/data-grid/data-grid.js.map +1 -1
- package/dist/src/data-grist.d.ts +3 -3
- package/dist/src/data-grist.js.map +1 -1
- package/dist/src/editors/ox-grist-editor.d.ts +9 -7
- package/dist/src/editors/ox-grist-editor.js +9 -7
- package/dist/src/editors/ox-grist-editor.js.map +1 -1
- package/dist/src/filters/filter-input.js +6 -5
- package/dist/src/filters/filter-input.js.map +1 -1
- package/dist/src/filters/filter-range-date.js +3 -3
- package/dist/src/filters/filter-range-date.js.map +1 -1
- package/dist/src/filters/filter-range-number.js +3 -3
- package/dist/src/filters/filter-range-number.js.map +1 -1
- package/dist/src/gutters/gutter-row-selector.js +6 -6
- package/dist/src/gutters/gutter-row-selector.js.map +1 -1
- package/dist/src/renderers/ox-grist-renderer-boolean.js +3 -3
- package/dist/src/renderers/ox-grist-renderer-boolean.js.map +1 -1
- package/dist/src/types.d.ts +5 -5
- package/dist/src/types.js.map +1 -1
- package/dist/src/utils/column-text.d.ts +10 -10
- package/dist/src/utils/column-text.js +10 -10
- package/dist/src/utils/column-text.js.map +1 -1
- package/dist/test/accessible-name.test.js +84 -86
- package/dist/test/accessible-name.test.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -3
- package/test/accessible-name.test.ts +84 -86
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@operato/data-grist",
|
|
3
|
-
"version": "10.10.
|
|
3
|
+
"version": "10.10.2",
|
|
4
4
|
"description": "User interface for grid (desktop) and list (mobile)",
|
|
5
5
|
"author": "heartyoh",
|
|
6
6
|
"type": "module",
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
"@material/web": "^2.0.0",
|
|
71
71
|
"@operato/headroom": "^10.0.0",
|
|
72
72
|
"@operato/i18n": "^10.0.0",
|
|
73
|
-
"@operato/input": "^10.10.
|
|
73
|
+
"@operato/input": "^10.10.1",
|
|
74
74
|
"@operato/p13n": "^10.10.0",
|
|
75
75
|
"@operato/popup": "^10.10.0",
|
|
76
76
|
"@operato/pull-to-refresh": "^10.0.0",
|
|
@@ -115,5 +115,5 @@
|
|
|
115
115
|
"prettier --write"
|
|
116
116
|
]
|
|
117
117
|
},
|
|
118
|
-
"gitHead": "
|
|
118
|
+
"gitHead": "d2c8c94cda6f6480e8be4e7d5994554ea5920c54"
|
|
119
119
|
}
|
|
@@ -8,62 +8,62 @@ import { columnText } from '../src/utils/column-text.js'
|
|
|
8
8
|
import { ZERO_CONFIG, ZERO_DATA } from '../src/configure/zero-config.js'
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
|
-
*
|
|
11
|
+
* Accessible names on the inputs inside the grid.
|
|
12
12
|
*
|
|
13
|
-
* ##
|
|
13
|
+
* ## Why this is tested
|
|
14
14
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
15
|
+
* Several inputs here had no name: the row-select checkboxes, the header filter boxes, and the IME
|
|
16
|
+
* buffer that holds focus. Position carries the meaning for a sighted reader — a narrow leftmost
|
|
17
|
+
* column means row select, a box under a header name filters that column — but position is a
|
|
18
|
+
* picture, not a relationship, so nothing of it reaches a screen reader.
|
|
19
19
|
*
|
|
20
|
-
* ##
|
|
20
|
+
* ## What has to hold
|
|
21
21
|
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
22
|
+
* The name has one source. Editing `header` must change what is announced, which means callers
|
|
23
|
+
* cannot spell the name out again. `columnText()` is that source.
|
|
24
24
|
*
|
|
25
|
-
* `buildColumn`
|
|
26
|
-
*
|
|
25
|
+
* `buildColumn` turns a string header into `() => header`, and that function returns a lit template
|
|
26
|
+
* the name cannot be read back out of. The name was disappearing right there.
|
|
27
27
|
*/
|
|
28
28
|
|
|
29
|
-
describe('column
|
|
30
|
-
it('
|
|
29
|
+
describe('column name has one source', () => {
|
|
30
|
+
it('keeps the plain text after a string header becomes a function', () => {
|
|
31
31
|
const column = buildColumn({ type: 'string', name: 'palletId', header: '파렛트 번호' })
|
|
32
32
|
|
|
33
33
|
expect(columnText(column)).to.equal('파렛트 번호')
|
|
34
34
|
})
|
|
35
35
|
|
|
36
|
-
it('
|
|
36
|
+
it('keeps it when the string arrives as header.renderer', () => {
|
|
37
37
|
const column = buildColumn({ type: 'string', name: 'palletId', header: { renderer: '파렛트 번호' } })
|
|
38
38
|
|
|
39
39
|
expect(columnText(column)).to.equal('파렛트 번호')
|
|
40
40
|
})
|
|
41
41
|
|
|
42
|
-
it('label: true
|
|
42
|
+
it('lets label: true inherit the header name', () => {
|
|
43
43
|
const column = buildColumn({ type: 'string', name: 'palletId', header: '파렛트 번호', label: true })
|
|
44
44
|
|
|
45
45
|
expect(columnText(column)).to.equal('파렛트 번호')
|
|
46
46
|
})
|
|
47
47
|
|
|
48
|
-
it('
|
|
48
|
+
it('falls back to column.name when the header is drawn by a function', () => {
|
|
49
49
|
const column = buildColumn({ type: 'string', name: 'palletId', header: { renderer: () => html`<i>x</i>` } })
|
|
50
50
|
|
|
51
51
|
expect(columnText(column)).to.equal('palletId')
|
|
52
52
|
})
|
|
53
53
|
|
|
54
|
-
it('
|
|
54
|
+
it('invents no name when there is no column', () => {
|
|
55
55
|
expect(columnText(undefined)).to.equal('')
|
|
56
56
|
expect(columnText(null)).to.equal('')
|
|
57
57
|
})
|
|
58
58
|
})
|
|
59
59
|
|
|
60
|
-
describe('
|
|
60
|
+
describe('row-select boxes are named', () => {
|
|
61
61
|
const columns = [
|
|
62
62
|
{ type: 'gutter', gutterName: 'row-selector', name: '__selected__', multiple: true },
|
|
63
63
|
{ type: 'string', name: 'palletId', header: '파렛트 번호' }
|
|
64
64
|
].map(buildColumn)
|
|
65
65
|
|
|
66
|
-
it('
|
|
66
|
+
it('says which row each checkbox selects', async () => {
|
|
67
67
|
const body: any = await fixture(html`
|
|
68
68
|
<ox-grid-body
|
|
69
69
|
.config=${{ ...ZERO_CONFIG, columns }}
|
|
@@ -74,7 +74,7 @@ describe('줄을 고르는 칸에는 이름이 있다', () => {
|
|
|
74
74
|
|
|
75
75
|
await body.updateComplete
|
|
76
76
|
|
|
77
|
-
/*
|
|
77
|
+
/* The checkboxes live one shadow root deeper, inside each ox-grid-field. */
|
|
78
78
|
const fields = Array.from(body.renderRoot.querySelectorAll('ox-grid-field')) as any[]
|
|
79
79
|
|
|
80
80
|
await Promise.all(fields.map(field => field.updateComplete))
|
|
@@ -83,14 +83,14 @@ describe('줄을 고르는 칸에는 이름이 있다', () => {
|
|
|
83
83
|
Array.from(field.renderRoot?.querySelectorAll('input[type=checkbox], input[type=radio]') || [])
|
|
84
84
|
) as HTMLInputElement[]
|
|
85
85
|
|
|
86
|
-
expect(boxes.length, '
|
|
86
|
+
expect(boxes.length, 'found no row-select box').to.be.greaterThan(0)
|
|
87
87
|
boxes.forEach(box =>
|
|
88
|
-
expect(box.getAttribute('aria-label'), '
|
|
88
|
+
expect(box.getAttribute('aria-label'), 'an unnamed checkbox is left').to.be.a('string').and.not.equal('')
|
|
89
89
|
)
|
|
90
90
|
})
|
|
91
91
|
})
|
|
92
92
|
|
|
93
|
-
describe('
|
|
93
|
+
describe('IME buffer names the cell it stands in for', () => {
|
|
94
94
|
const columns = [
|
|
95
95
|
{ type: 'string', name: 'palletId', header: '파렛트 번호' },
|
|
96
96
|
{ type: 'string', name: 'location', header: '보관 위치' }
|
|
@@ -111,41 +111,40 @@ describe('포커스를 담고 있는 IME buffer 가 지금 선 칸을 말한다'
|
|
|
111
111
|
return body.renderRoot.querySelector('#ime-buffer') as HTMLInputElement
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
-
it('
|
|
114
|
+
it('reads the first column while focus sits there', async () => {
|
|
115
115
|
expect((await bodyFocusedAt(0)).getAttribute('aria-label')).to.contain('파렛트 번호')
|
|
116
116
|
})
|
|
117
117
|
|
|
118
|
-
it('
|
|
118
|
+
it('changes what it reads when focus moves a column over', async () => {
|
|
119
119
|
expect((await bodyFocusedAt(1)).getAttribute('aria-label')).to.contain('보관 위치')
|
|
120
120
|
})
|
|
121
121
|
|
|
122
|
-
it('
|
|
122
|
+
it('stays visible to assistive tech — it is where focus lands', async () => {
|
|
123
123
|
expect((await bodyFocusedAt(0)).getAttribute('aria-hidden')).to.be.null
|
|
124
124
|
})
|
|
125
125
|
})
|
|
126
126
|
|
|
127
127
|
/**
|
|
128
|
-
*
|
|
128
|
+
* Table semantics.
|
|
129
129
|
*
|
|
130
|
-
* ##
|
|
130
|
+
* ## Why this is tested
|
|
131
131
|
*
|
|
132
|
-
*
|
|
133
|
-
*
|
|
134
|
-
*
|
|
132
|
+
* The grid is drawn with CSS grid: cells sit flat and there is no row element. Rows are a picture
|
|
133
|
+
* made of equal offsetTop, so a screen reader saw a pile of inputs rather than a table — no "this
|
|
134
|
+
* is a table", no "row 3 column 3", no "40 rows in all".
|
|
135
135
|
*
|
|
136
|
-
*
|
|
136
|
+
* Adding row wrappers can break two things at once:
|
|
137
137
|
*
|
|
138
138
|
* ```
|
|
139
|
-
*
|
|
140
|
-
*
|
|
139
|
+
* semantics grid / row / columnheader / gridcell have to nest correctly
|
|
140
|
+
* layout display: contents must generate no box; if it does, the columns collapse
|
|
141
141
|
* ```
|
|
142
142
|
*
|
|
143
|
-
*
|
|
144
|
-
*
|
|
145
|
-
* 줄과 줄은 y 가 달라야 한다.
|
|
143
|
+
* The second one is why geometry is asserted: cells in a row must step right, and rows must step
|
|
144
|
+
* down.
|
|
146
145
|
*/
|
|
147
|
-
describe('
|
|
148
|
-
/* ox-grid
|
|
146
|
+
describe('grid declares itself a grid', () => {
|
|
147
|
+
/* ox-grid takes already-compiled columns; compiling them is ox-grist's job. */
|
|
149
148
|
const columns = [
|
|
150
149
|
{ type: 'gutter', gutterName: 'row-selector', name: '__selected__', multiple: true },
|
|
151
150
|
{ type: 'string', name: 'palletId', header: '파렛트 번호', sortable: true },
|
|
@@ -181,12 +180,12 @@ describe('표를 표라고 선언한다', () => {
|
|
|
181
180
|
return { el, header, body }
|
|
182
181
|
}
|
|
183
182
|
|
|
184
|
-
it('
|
|
183
|
+
it('nests grid, row and cell', async () => {
|
|
185
184
|
const { el, header, body } = await grid()
|
|
186
185
|
|
|
187
186
|
const grid_ = el.renderRoot.querySelector('[role=grid]')
|
|
188
187
|
|
|
189
|
-
expect(grid_, 'role=grid
|
|
188
|
+
expect(grid_, 'no role=grid').to.exist
|
|
190
189
|
expect(header.getAttribute('role')).to.equal('rowgroup')
|
|
191
190
|
expect(body.getAttribute('role')).to.equal('rowgroup')
|
|
192
191
|
|
|
@@ -195,38 +194,38 @@ describe('표를 표라고 선언한다', () => {
|
|
|
195
194
|
expect(header.renderRoot.querySelectorAll('[role=columnheader]').length).to.equal(3)
|
|
196
195
|
})
|
|
197
196
|
|
|
198
|
-
it('
|
|
197
|
+
it('counts the whole table, not the page on screen', async () => {
|
|
199
198
|
const { el } = await grid()
|
|
200
199
|
|
|
201
200
|
const grid_ = el.renderRoot.querySelector('[role=grid]')
|
|
202
201
|
|
|
203
|
-
/*
|
|
202
|
+
/* One header row plus 40 records. Two rows drawn is still a 40-row table. */
|
|
204
203
|
expect(grid_.getAttribute('aria-rowcount')).to.equal('41')
|
|
205
204
|
expect(grid_.getAttribute('aria-colcount')).to.equal('3')
|
|
206
205
|
expect(grid_.getAttribute('aria-label')).to.equal('입고 대기 목록')
|
|
207
206
|
})
|
|
208
207
|
|
|
209
|
-
it('2
|
|
208
|
+
it('numbers page 2 from row 22, not back at 1', async () => {
|
|
210
209
|
const { body } = await grid({ page: 2 })
|
|
211
210
|
|
|
212
211
|
const rows = Array.from(body.renderRoot.querySelectorAll('[role=row]')) as HTMLElement[]
|
|
213
212
|
|
|
214
|
-
/*
|
|
213
|
+
/* One header row plus 20 records on page 1. */
|
|
215
214
|
expect(rows[0].getAttribute('aria-rowindex')).to.equal('22')
|
|
216
215
|
expect(rows[1].getAttribute('aria-rowindex')).to.equal('23')
|
|
217
216
|
})
|
|
218
217
|
|
|
219
|
-
it('
|
|
218
|
+
it('numbers columns and leaves the width filler uncounted', async () => {
|
|
220
219
|
const { body } = await grid()
|
|
221
220
|
|
|
222
221
|
const row = body.renderRoot.querySelector('[role=row]') as HTMLElement
|
|
223
222
|
const cells = Array.from(row.querySelectorAll('[role=gridcell]')) as HTMLElement[]
|
|
224
223
|
|
|
225
224
|
expect(cells.map(cell => cell.getAttribute('aria-colindex'))).to.deep.equal(['1', '2', '3'])
|
|
226
|
-
expect(row.querySelectorAll('[role=presentation]').length, '
|
|
225
|
+
expect(row.querySelectorAll('[role=presentation]').length, 'the filler was counted as a cell').to.equal(1)
|
|
227
226
|
})
|
|
228
227
|
|
|
229
|
-
it('
|
|
228
|
+
it('states sort direction in text, not only as an arrow', async () => {
|
|
230
229
|
const el: any = await fixture(html`
|
|
231
230
|
<ox-grid
|
|
232
231
|
.config=${{ ...ZERO_CONFIG, columns, rows: { ...ZERO_CONFIG.rows } }}
|
|
@@ -243,40 +242,39 @@ describe('표를 표라고 선언한다', () => {
|
|
|
243
242
|
const headers = Array.from(header.renderRoot.querySelectorAll('[role=columnheader]')) as HTMLElement[]
|
|
244
243
|
const sorts = headers.map(cell => cell.getAttribute('aria-sort'))
|
|
245
244
|
|
|
246
|
-
/*
|
|
245
|
+
/* A gutter cannot be sorted; claiming otherwise announces a control that is not there. */
|
|
247
246
|
expect(sorts[0]).to.be.null
|
|
248
|
-
expect(sorts[1], '
|
|
249
|
-
expect(sorts[2], '
|
|
247
|
+
expect(sorts[1], 'sorted descending').to.equal('descending')
|
|
248
|
+
expect(sorts[2], 'sortable but not currently sorted').to.equal('none')
|
|
250
249
|
})
|
|
251
250
|
|
|
252
|
-
it('
|
|
251
|
+
it('keeps the layout — a row wrapper that generates a box collapses the grid', async () => {
|
|
253
252
|
const { body } = await grid()
|
|
254
253
|
|
|
255
254
|
const rows = Array.from(body.renderRoot.querySelectorAll('[role=row]')) as HTMLElement[]
|
|
256
255
|
const first = Array.from(rows[0].children) as HTMLElement[]
|
|
257
256
|
|
|
258
|
-
/* 감싸개 자신은 상자가 없다 */
|
|
259
257
|
expect(getComputedStyle(rows[0]).display).to.equal('contents')
|
|
260
258
|
|
|
261
|
-
/*
|
|
259
|
+
/* Cells step right; stacked at one x means the columns are gone. */
|
|
262
260
|
const lefts = first.map(cell => cell.offsetLeft)
|
|
263
|
-
expect(new Set(lefts).size, '
|
|
261
|
+
expect(new Set(lefts).size, 'cells stacked at the same x').to.be.greaterThan(1)
|
|
264
262
|
lefts.slice(1).forEach((left, index) => expect(left).to.be.greaterThan(lefts[index]))
|
|
265
263
|
|
|
266
|
-
/*
|
|
264
|
+
/* Rows step down. */
|
|
267
265
|
const firstTop = (rows[0].children[0] as HTMLElement).offsetTop
|
|
268
266
|
const secondTop = (rows[1].children[0] as HTMLElement).offsetTop
|
|
269
|
-
expect(secondTop, '
|
|
267
|
+
expect(secondTop, 'two rows at the same y').to.be.greaterThan(firstTop)
|
|
270
268
|
})
|
|
271
269
|
})
|
|
272
270
|
|
|
273
271
|
/**
|
|
274
|
-
*
|
|
272
|
+
* Grouped headers.
|
|
275
273
|
*
|
|
276
|
-
*
|
|
277
|
-
* `grid-row` / `grid-column
|
|
274
|
+
* The header becomes two rows and ungrouped columns span both (`.span-both`). Placement runs
|
|
275
|
+
* through CSS `grid-row` / `grid-column`, which makes this the riskiest spot for a row wrapper.
|
|
278
276
|
*/
|
|
279
|
-
describe('
|
|
277
|
+
describe('grouped header spans two rows', () => {
|
|
280
278
|
const columns = [
|
|
281
279
|
{ type: 'string', name: 'palletId', header: '파렛트 번호' },
|
|
282
280
|
{ type: 'string', name: 'width', header: { renderer: '폭', group: '치수' } },
|
|
@@ -302,67 +300,67 @@ describe('묶음 머리 — 머리가 두 줄이다', () => {
|
|
|
302
300
|
return { el, header, body }
|
|
303
301
|
}
|
|
304
302
|
|
|
305
|
-
it('
|
|
303
|
+
it('counts two header rows so the body starts at row 3', async () => {
|
|
306
304
|
const { el, header, body } = await grouped()
|
|
307
305
|
|
|
308
306
|
expect(header.renderRoot.querySelectorAll('[role=row]').length).to.equal(2)
|
|
309
307
|
|
|
310
|
-
/*
|
|
308
|
+
/* Two header rows plus one record. */
|
|
311
309
|
expect(el.renderRoot.querySelector('[role=grid]').getAttribute('aria-rowcount')).to.equal('3')
|
|
312
310
|
|
|
313
311
|
const rows = Array.from(body.renderRoot.querySelectorAll('[role=row]')) as HTMLElement[]
|
|
314
312
|
expect(rows[0].getAttribute('aria-rowindex')).to.equal('3')
|
|
315
313
|
})
|
|
316
314
|
|
|
317
|
-
it('
|
|
315
|
+
it('says how many columns a group covers', async () => {
|
|
318
316
|
const { header } = await grouped()
|
|
319
317
|
|
|
320
318
|
const cells = Array.from(header.renderRoot.querySelectorAll('[role=columnheader]')) as HTMLElement[]
|
|
321
319
|
const first = cells[0]
|
|
322
320
|
const group = cells.find(cell => cell.getAttribute('aria-colspan'))
|
|
323
321
|
|
|
324
|
-
/*
|
|
322
|
+
/* An ungrouped column spans both header rows. */
|
|
325
323
|
expect(first.getAttribute('aria-rowspan')).to.equal('2')
|
|
326
324
|
expect(first.getAttribute('aria-colindex')).to.equal('1')
|
|
327
325
|
|
|
328
|
-
/*
|
|
329
|
-
expect(group, '
|
|
326
|
+
/* The group header covers columns 2 and 3. */
|
|
327
|
+
expect(group, 'found no group header').to.exist
|
|
330
328
|
expect(group!.getAttribute('aria-colindex')).to.equal('2')
|
|
331
329
|
expect(group!.getAttribute('aria-colspan')).to.equal('2')
|
|
332
330
|
})
|
|
333
331
|
|
|
334
|
-
it('
|
|
332
|
+
it('keeps two-row header placement — group sits above its leaf columns', async () => {
|
|
335
333
|
const { header } = await grouped()
|
|
336
334
|
|
|
337
335
|
const cells = Array.from(header.renderRoot.querySelectorAll('[role=columnheader]')) as HTMLElement[]
|
|
338
336
|
const group = cells.find(cell => cell.getAttribute('aria-colspan'))!
|
|
339
337
|
const leaf = cells.find(cell => !cell.getAttribute('aria-colspan') && cell !== cells[0])!
|
|
340
338
|
|
|
341
|
-
expect(leaf.offsetTop, '
|
|
342
|
-
expect(group.offsetWidth, '
|
|
339
|
+
expect(leaf.offsetTop, 'group header and its leaf column at the same height').to.be.greaterThan(group.offsetTop)
|
|
340
|
+
expect(group.offsetWidth, 'group header does not cover two columns').to.be.greaterThan(leaf.offsetWidth)
|
|
343
341
|
})
|
|
344
342
|
})
|
|
345
343
|
|
|
346
344
|
/**
|
|
347
|
-
*
|
|
345
|
+
* The grid wrapper must not disturb the surrounding layout.
|
|
348
346
|
*
|
|
349
|
-
* `role=grid`
|
|
350
|
-
*
|
|
347
|
+
* `role=grid` admits only rows, so the settings-button slot and the pager moved outside the
|
|
348
|
+
* wrapper. Two things then depend on this structure:
|
|
351
349
|
*
|
|
352
350
|
* ```
|
|
353
|
-
*
|
|
354
|
-
*
|
|
355
|
-
*
|
|
356
|
-
*
|
|
351
|
+
* column layout the host is a flex column. A wrapper that generates a box swallows header
|
|
352
|
+
* and body into one flex item, and the body's flex: 1 stops working
|
|
353
|
+
* sibling selector "show the settings button on header hover" is an adjacent-sibling rule, so
|
|
354
|
+
* the button has to sit directly after the wrapper
|
|
357
355
|
* ```
|
|
358
356
|
*/
|
|
359
|
-
describe('
|
|
357
|
+
describe('grid wrapper stays out of the layout', () => {
|
|
360
358
|
const columns = [
|
|
361
359
|
{ type: 'string', name: 'palletId', header: '파렛트 번호' },
|
|
362
360
|
{ type: 'string', name: 'location', header: '보관 위치' }
|
|
363
361
|
].map(buildColumn)
|
|
364
362
|
|
|
365
|
-
it('
|
|
363
|
+
it('places the settings button after the grid, not inside it', async () => {
|
|
366
364
|
const el: any = await fixture(html`
|
|
367
365
|
<ox-grid
|
|
368
366
|
.config=${{ ...ZERO_CONFIG, columns, rows: { ...ZERO_CONFIG.rows } }}
|
|
@@ -375,12 +373,12 @@ describe('표 감싸개는 배치에 끼어들지 않는다', () => {
|
|
|
375
373
|
const grid = el.renderRoot.querySelector('[role=grid]') as HTMLElement
|
|
376
374
|
const setting = el.renderRoot.querySelector('div[setting]') as HTMLElement
|
|
377
375
|
|
|
378
|
-
expect(setting, '
|
|
379
|
-
expect(grid.contains(setting), '
|
|
380
|
-
expect(grid.nextElementSibling, '
|
|
376
|
+
expect(setting, 'no settings slot').to.exist
|
|
377
|
+
expect(grid.contains(setting), 'settings button inside the grid breaks table reading').to.be.false
|
|
378
|
+
expect(grid.nextElementSibling, 'adjacent-sibling rule no longer holds').to.equal(setting)
|
|
381
379
|
})
|
|
382
380
|
|
|
383
|
-
it('
|
|
381
|
+
it('leaves header and body as flex items of the host', async () => {
|
|
384
382
|
const el: any = await fixture(html`
|
|
385
383
|
<div style="height: 300px; display: flex">
|
|
386
384
|
<ox-grid
|
|
@@ -400,9 +398,9 @@ describe('표 감싸개는 배치에 끼어들지 않는다', () => {
|
|
|
400
398
|
|
|
401
399
|
expect(getComputedStyle(wrapper).display).to.equal('contents')
|
|
402
400
|
|
|
403
|
-
/*
|
|
404
|
-
expect(body.offsetHeight, '
|
|
405
|
-
expect(body.offsetTop, '
|
|
401
|
+
/* The body takes the remaining height; a wrapper with a box shrinks it to header height. */
|
|
402
|
+
expect(body.offsetHeight, 'body does not take the remaining height').to.be.greaterThan(100)
|
|
403
|
+
expect(body.offsetTop, 'body is not below the header').to.be.greaterThan(0)
|
|
406
404
|
expect(header.offsetTop).to.equal(0)
|
|
407
405
|
})
|
|
408
406
|
})
|