@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.
Files changed (57) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/coverage/lcov-report/base.css +224 -0
  3. package/coverage/lcov-report/block-navigation.js +87 -0
  4. package/coverage/lcov-report/favicon.png +0 -0
  5. package/coverage/lcov-report/index.html +866 -0
  6. package/coverage/lcov-report/prettify.css +1 -0
  7. package/coverage/lcov-report/prettify.js +2 -0
  8. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  9. package/coverage/lcov-report/sorter.js +210 -0
  10. package/coverage/lcov-report/webpack/bootstrap.html +118 -0
  11. package/coverage/lcov-report/webpack/index.html +116 -0
  12. package/coverage/lcov-report/webpack/runtime/define property getters.html +118 -0
  13. package/coverage/lcov-report/webpack/runtime/global.html +118 -0
  14. package/coverage/lcov-report/webpack/runtime/hasOwnProperty shorthand.html +118 -0
  15. package/coverage/lcov-report/webpack/runtime/index.html +161 -0
  16. package/coverage/lcov-report/webpack/runtime/make namespace object.html +118 -0
  17. package/coverage/lcov.info +53451 -0
  18. package/dist/src/configure/column-builder.js +6 -7
  19. package/dist/src/configure/column-builder.js.map +1 -1
  20. package/dist/src/data-grid/data-grid-body-style.js +4 -4
  21. package/dist/src/data-grid/data-grid-body-style.js.map +1 -1
  22. package/dist/src/data-grid/data-grid-body.d.ts +11 -11
  23. package/dist/src/data-grid/data-grid-body.js +33 -35
  24. package/dist/src/data-grid/data-grid-body.js.map +1 -1
  25. package/dist/src/data-grid/data-grid-field.js +13 -0
  26. package/dist/src/data-grid/data-grid-field.js.map +1 -1
  27. package/dist/src/data-grid/data-grid-header.d.ts +6 -6
  28. package/dist/src/data-grid/data-grid-header.js +22 -21
  29. package/dist/src/data-grid/data-grid-header.js.map +1 -1
  30. package/dist/src/data-grid/data-grid.d.ts +4 -4
  31. package/dist/src/data-grid/data-grid.js +24 -24
  32. package/dist/src/data-grid/data-grid.js.map +1 -1
  33. package/dist/src/data-grist.d.ts +3 -3
  34. package/dist/src/data-grist.js.map +1 -1
  35. package/dist/src/editors/ox-grist-editor.d.ts +9 -7
  36. package/dist/src/editors/ox-grist-editor.js +9 -7
  37. package/dist/src/editors/ox-grist-editor.js.map +1 -1
  38. package/dist/src/filters/filter-input.js +6 -5
  39. package/dist/src/filters/filter-input.js.map +1 -1
  40. package/dist/src/filters/filter-range-date.js +3 -3
  41. package/dist/src/filters/filter-range-date.js.map +1 -1
  42. package/dist/src/filters/filter-range-number.js +3 -3
  43. package/dist/src/filters/filter-range-number.js.map +1 -1
  44. package/dist/src/gutters/gutter-row-selector.js +6 -6
  45. package/dist/src/gutters/gutter-row-selector.js.map +1 -1
  46. package/dist/src/renderers/ox-grist-renderer-boolean.js +3 -3
  47. package/dist/src/renderers/ox-grist-renderer-boolean.js.map +1 -1
  48. package/dist/src/types.d.ts +5 -5
  49. package/dist/src/types.js.map +1 -1
  50. package/dist/src/utils/column-text.d.ts +10 -10
  51. package/dist/src/utils/column-text.js +10 -10
  52. package/dist/src/utils/column-text.js.map +1 -1
  53. package/dist/test/accessible-name.test.js +84 -86
  54. package/dist/test/accessible-name.test.js.map +1 -1
  55. package/dist/tsconfig.tsbuildinfo +1 -1
  56. package/package.json +3 -3
  57. package/test/accessible-name.test.ts +84 -86
@@ -6,51 +6,51 @@ import { buildColumn } from '../src/configure/column-builder.js';
6
6
  import { columnText } from '../src/utils/column-text.js';
7
7
  import { ZERO_CONFIG, ZERO_DATA } from '../src/configure/zero-config.js';
8
8
  /**
9
- * 입력칸의 이름.
9
+ * Accessible names on the inputs inside the grid.
10
10
  *
11
- * ## 왜 시험이 필요한가
11
+ * ## Why this is tested
12
12
  *
13
- * 이 grid 안에는 이름 없는 입력칸이 여럿 있었다 — 줄을 고르는 체크박스, 머리줄의 filter 상자,
14
- * 그리고 포커스를 담고 있는 IME buffer. 눈으로 보는 사람에게는 **자리**가 뜻을 말해 준다(맨 왼쪽
15
- * 좁은 칸이면 줄 선택, 머리줄 이름 바로 아래면 그 column 의 filter). 그런데 자리는 그림일 뿐이고
16
- * 관계가 아니라서, 화면을 못 보는 사람에게는 이름 없는 상자들만 남는다.
13
+ * Several inputs here had no name: the row-select checkboxes, the header filter boxes, and the IME
14
+ * buffer that holds focus. Position carries the meaning for a sighted reader — a narrow leftmost
15
+ * column means row select, a box under a header name filters that column — but position is a
16
+ * picture, not a relationship, so nothing of it reaches a screen reader.
17
17
  *
18
- * ## 무엇을 지키려는가
18
+ * ## What has to hold
19
19
  *
20
- * 이름의 출처가 하나여야 한다. `header` 를 고쳤을 때 읽히는 이름도 함께 바뀌어야 하고, 그러려면
21
- * 부르는 쪽이 이름을 다시 적어 두면 안 된다. `columnText()` 가 그 유일한 출처다.
20
+ * The name has one source. Editing `header` must change what is announced, which means callers
21
+ * cannot spell the name out again. `columnText()` is that source.
22
22
  *
23
- * `buildColumn` 이 문자열 header 를 `() => header` 함수로 바꿔 버리는 자리가 있었는데, 그 함수가
24
- * 돌려주는 것은 lit template 이라 이름을 되꺼낼 수 없다 — 여기서 이름이 사라지고 있었다.
23
+ * `buildColumn` turns a string header into `() => header`, and that function returns a lit template
24
+ * the name cannot be read back out of. The name was disappearing right there.
25
25
  */
26
- describe('column 이름의 출처는 하나다', () => {
27
- it('문자열로 준 header 는 함수로 바뀌어도 글자가 남는다', () => {
26
+ describe('column name has one source', () => {
27
+ it('keeps the plain text after a string header becomes a function', () => {
28
28
  const column = buildColumn({ type: 'string', name: 'palletId', header: '파렛트 번호' });
29
29
  expect(columnText(column)).to.equal('파렛트 번호');
30
30
  });
31
- it('header.renderer 에 문자열을 준 경우도 같다', () => {
31
+ it('keeps it when the string arrives as header.renderer', () => {
32
32
  const column = buildColumn({ type: 'string', name: 'palletId', header: { renderer: '파렛트 번호' } });
33
33
  expect(columnText(column)).to.equal('파렛트 번호');
34
34
  });
35
- it('label: true 는 header 의 이름을 물려받는다', () => {
35
+ it('lets label: true inherit the header name', () => {
36
36
  const column = buildColumn({ type: 'string', name: 'palletId', header: '파렛트 번호', label: true });
37
37
  expect(columnText(column)).to.equal('파렛트 번호');
38
38
  });
39
- it('header 를 함수로 그리면 이름을 되꺼낼 수 없다 — 그때는 column 이름으로 물러난다', () => {
39
+ it('falls back to column.name when the header is drawn by a function', () => {
40
40
  const column = buildColumn({ type: 'string', name: 'palletId', header: { renderer: () => html `<i>x</i>` } });
41
41
  expect(columnText(column)).to.equal('palletId');
42
42
  });
43
- it('column 이 없으면 이름을 지어내지 않는다', () => {
43
+ it('invents no name when there is no column', () => {
44
44
  expect(columnText(undefined)).to.equal('');
45
45
  expect(columnText(null)).to.equal('');
46
46
  });
47
47
  });
48
- describe('줄을 고르는 칸에는 이름이 있다', () => {
48
+ describe('row-select boxes are named', () => {
49
49
  const columns = [
50
50
  { type: 'gutter', gutterName: 'row-selector', name: '__selected__', multiple: true },
51
51
  { type: 'string', name: 'palletId', header: '파렛트 번호' }
52
52
  ].map(buildColumn);
53
- it('행마다 놓인 체크박스가 몇 번째 줄인지 말한다', async () => {
53
+ it('says which row each checkbox selects', async () => {
54
54
  const body = await fixture(html `
55
55
  <ox-grid-body
56
56
  .config=${{ ...ZERO_CONFIG, columns }}
@@ -59,15 +59,15 @@ describe('줄을 고르는 칸에는 이름이 있다', () => {
59
59
  ></ox-grid-body>
60
60
  `);
61
61
  await body.updateComplete;
62
- /* 체크박스는 각 칸(ox-grid-field)의 shadow root 안에 그려진다 — 한 겹 더 들어가야 보인다. */
62
+ /* The checkboxes live one shadow root deeper, inside each ox-grid-field. */
63
63
  const fields = Array.from(body.renderRoot.querySelectorAll('ox-grid-field'));
64
64
  await Promise.all(fields.map(field => field.updateComplete));
65
65
  const boxes = fields.flatMap(field => { var _a; return Array.from(((_a = field.renderRoot) === null || _a === void 0 ? void 0 : _a.querySelectorAll('input[type=checkbox], input[type=radio]')) || []); });
66
- expect(boxes.length, '줄을 고르는 칸을 찾지 못했다').to.be.greaterThan(0);
67
- boxes.forEach(box => expect(box.getAttribute('aria-label'), '이름 없는 체크박스가 남아 있다').to.be.a('string').and.not.equal(''));
66
+ expect(boxes.length, 'found no row-select box').to.be.greaterThan(0);
67
+ boxes.forEach(box => expect(box.getAttribute('aria-label'), 'an unnamed checkbox is left').to.be.a('string').and.not.equal(''));
68
68
  });
69
69
  });
70
- describe('포커스를 담고 있는 IME buffer 가 지금 선 칸을 말한다', () => {
70
+ describe('IME buffer names the cell it stands in for', () => {
71
71
  const columns = [
72
72
  { type: 'string', name: 'palletId', header: '파렛트 번호' },
73
73
  { type: 'string', name: 'location', header: '보관 위치' }
@@ -84,38 +84,37 @@ describe('포커스를 담고 있는 IME buffer 가 지금 선 칸을 말한다'
84
84
  await body.updateComplete;
85
85
  return body.renderRoot.querySelector('#ime-buffer');
86
86
  }
87
- it('첫 칸에 서 있으면 첫 칸의 이름을 읽는다', async () => {
87
+ it('reads the first column while focus sits there', async () => {
88
88
  expect((await bodyFocusedAt(0)).getAttribute('aria-label')).to.contain('파렛트 번호');
89
89
  });
90
- it('옆 칸으로 옮기면 읽히는 이름도 바뀐다 — 안 바뀌면 어디에 선 것인지 알 수 없다', async () => {
90
+ it('changes what it reads when focus moves a column over', async () => {
91
91
  expect((await bodyFocusedAt(1)).getAttribute('aria-label')).to.contain('보관 위치');
92
92
  });
93
- it('보조기술에서 지우지 않는다 — 포커스를 받는 자리다', async () => {
93
+ it('stays visible to assistive tech — it is where focus lands', async () => {
94
94
  expect((await bodyFocusedAt(0)).getAttribute('aria-hidden')).to.be.null;
95
95
  });
96
96
  });
97
97
  /**
98
- * 표를 표라고 선언했나.
98
+ * Table semantics.
99
99
  *
100
- * ## 왜 시험이 필요한가
100
+ * ## Why this is tested
101
101
  *
102
- * 이 표는 CSS grid 로 그린다 — 칸이 평평하게 놓여 있고 **줄이라는 요소가 없다.** 보기에는 줄이
103
- * 있지만 그것은 같은 높이에 놓였다는 그림일 뿐이다. 그래서 스크린 리더에게 이 화면은 「여기가
104
- * 표다」도, 「지금 3행 3열이다」도, 「전체는 40행이다」도 아닌 **입력칸 더미**였다.
102
+ * The grid is drawn with CSS grid: cells sit flat and there is no row element. Rows are a picture
103
+ * made of equal offsetTop, so a screen reader saw a pile of inputs rather than a table — no "this
104
+ * is a table", no "row 3 column 3", no "40 rows in all".
105
105
  *
106
- * 줄 감싸개를 넣으면서 두 가지가 동시에 깨질 수 있다.
106
+ * Adding row wrappers can break two things at once:
107
107
  *
108
108
  * ```
109
- * 뜻이 서나 role=grid / row / columnheader / gridcell 이 제대로 겹쳐 있나
110
- * 모양이 사나 display: contents 가 정말로 상자를 안 만드나 — 만들면 표가 한 줄로 무너진다
109
+ * semantics grid / row / columnheader / gridcell have to nest correctly
110
+ * layout display: contents must generate no box; if it does, the columns collapse
111
111
  * ```
112
112
  *
113
- * 두 번째가 특히 중요하다. `display: contents` 를 잘못 걸면 칸들이 grid 항목이 아니게 되어
114
- * 열이 사라진다. 그래서 **자리를 실제로 확인한다** — 한 줄 안의 칸은 왼쪽으로 갈수록 x 가 커지고,
115
- * 줄과 줄은 y 가 달라야 한다.
113
+ * The second one is why geometry is asserted: cells in a row must step right, and rows must step
114
+ * down.
116
115
  */
117
- describe('표를 표라고 선언한다', () => {
118
- /* ox-grid 는 이미 다듬어진 column 을 받는다 — 다듬는 것은 ox-grist 의 일이다. */
116
+ describe('grid declares itself a grid', () => {
117
+ /* ox-grid takes already-compiled columns; compiling them is ox-grist's job. */
119
118
  const columns = [
120
119
  { type: 'gutter', gutterName: 'row-selector', name: '__selected__', multiple: true },
121
120
  { type: 'string', name: 'palletId', header: '파렛트 번호', sortable: true },
@@ -145,39 +144,39 @@ describe('표를 표라고 선언한다', () => {
145
144
  await (body === null || body === void 0 ? void 0 : body.updateComplete);
146
145
  return { el, header, body };
147
146
  }
148
- it('표 · 줄 · 칸이 겹쳐 있다', async () => {
147
+ it('nests grid, row and cell', async () => {
149
148
  const { el, header, body } = await grid();
150
149
  const grid_ = el.renderRoot.querySelector('[role=grid]');
151
- expect(grid_, 'role=grid 가 없다').to.exist;
150
+ expect(grid_, 'no role=grid').to.exist;
152
151
  expect(header.getAttribute('role')).to.equal('rowgroup');
153
152
  expect(body.getAttribute('role')).to.equal('rowgroup');
154
153
  expect(header.renderRoot.querySelectorAll('[role=row]').length).to.equal(1);
155
154
  expect(body.renderRoot.querySelectorAll('[role=row]').length).to.equal(2);
156
155
  expect(header.renderRoot.querySelectorAll('[role=columnheader]').length).to.equal(3);
157
156
  });
158
- it('전체가 몇 줄 몇 열인지 말한다 — 지금 페이지가 아니라', async () => {
157
+ it('counts the whole table, not the page on screen', async () => {
159
158
  const { el } = await grid();
160
159
  const grid_ = el.renderRoot.querySelector('[role=grid]');
161
- /* 머리 한 줄 + 전체 40건. 화면에 두 줄만 그려도 40건짜리 표다. */
160
+ /* One header row plus 40 records. Two rows drawn is still a 40-row table. */
162
161
  expect(grid_.getAttribute('aria-rowcount')).to.equal('41');
163
162
  expect(grid_.getAttribute('aria-colcount')).to.equal('3');
164
163
  expect(grid_.getAttribute('aria-label')).to.equal('입고 대기 목록');
165
164
  });
166
- it('2페이지의 첫 줄은 21번째 줄이다 — 페이지마다 1번으로 돌아가지 않는다', async () => {
165
+ it('numbers page 2 from row 22, not back at 1', async () => {
167
166
  const { body } = await grid({ page: 2 });
168
167
  const rows = Array.from(body.renderRoot.querySelectorAll('[role=row]'));
169
- /* 머리 한 줄(1) + 앞 페이지 20건 = 22번째부터 */
168
+ /* One header row plus 20 records on page 1. */
170
169
  expect(rows[0].getAttribute('aria-rowindex')).to.equal('22');
171
170
  expect(rows[1].getAttribute('aria-rowindex')).to.equal('23');
172
171
  });
173
- it('칸이 몇 번째 열인지 말한다 — 남는 폭을 메우는 칸은 세지 않는다', async () => {
172
+ it('numbers columns and leaves the width filler uncounted', async () => {
174
173
  const { body } = await grid();
175
174
  const row = body.renderRoot.querySelector('[role=row]');
176
175
  const cells = Array.from(row.querySelectorAll('[role=gridcell]'));
177
176
  expect(cells.map(cell => cell.getAttribute('aria-colindex'))).to.deep.equal(['1', '2', '3']);
178
- expect(row.querySelectorAll('[role=presentation]').length, '메우는 칸이 표의 칸으로 세어졌다').to.equal(1);
177
+ expect(row.querySelectorAll('[role=presentation]').length, 'the filler was counted as a cell').to.equal(1);
179
178
  });
180
- it('정렬 상태를 글자로도 말한다 — 화살표는 눈으로만 읽힌다', async () => {
179
+ it('states sort direction in text, not only as an arrow', async () => {
181
180
  const el = await fixture(html `
182
181
  <ox-grid
183
182
  .config=${{ ...ZERO_CONFIG, columns, rows: { ...ZERO_CONFIG.rows } }}
@@ -190,34 +189,33 @@ describe('표를 표라고 선언한다', () => {
190
189
  await header.updateComplete;
191
190
  const headers = Array.from(header.renderRoot.querySelectorAll('[role=columnheader]'));
192
191
  const sorts = headers.map(cell => cell.getAttribute('aria-sort'));
193
- /* 거터는 정렬할 수 없다 — 있지도 않은 기능을 있다고 말하지 않는다. */
192
+ /* A gutter cannot be sorted; claiming otherwise announces a control that is not there. */
194
193
  expect(sorts[0]).to.be.null;
195
- expect(sorts[1], '내림차순으로 정렬된 열').to.equal('descending');
196
- expect(sorts[2], '정렬할 수 있으나 지금은 아닌 열').to.equal('none');
194
+ expect(sorts[1], 'sorted descending').to.equal('descending');
195
+ expect(sorts[2], 'sortable but not currently sorted').to.equal('none');
197
196
  });
198
- it('줄 감싸개가 배치를 건드리지 않는다 — 상자를 만들면 표가 무너진다', async () => {
197
+ it('keeps the layout — a row wrapper that generates a box collapses the grid', async () => {
199
198
  const { body } = await grid();
200
199
  const rows = Array.from(body.renderRoot.querySelectorAll('[role=row]'));
201
200
  const first = Array.from(rows[0].children);
202
- /* 감싸개 자신은 상자가 없다 */
203
201
  expect(getComputedStyle(rows[0]).display).to.equal('contents');
204
- /* 한 줄 안의 칸은 오른쪽으로 나아간다 — 한 자리에 겹쳐 있으면 열이 사라진 것이다 */
202
+ /* Cells step right; stacked at one x means the columns are gone. */
205
203
  const lefts = first.map(cell => cell.offsetLeft);
206
- expect(new Set(lefts).size, '칸들이 같은 x 에 겹쳐 있다').to.be.greaterThan(1);
204
+ expect(new Set(lefts).size, 'cells stacked at the same x').to.be.greaterThan(1);
207
205
  lefts.slice(1).forEach((left, index) => expect(left).to.be.greaterThan(lefts[index]));
208
- /* 줄과 줄은 아래로 내려간다 */
206
+ /* Rows step down. */
209
207
  const firstTop = rows[0].children[0].offsetTop;
210
208
  const secondTop = rows[1].children[0].offsetTop;
211
- expect(secondTop, '두 줄이 같은 y 에 있다').to.be.greaterThan(firstTop);
209
+ expect(secondTop, 'two rows at the same y').to.be.greaterThan(firstTop);
212
210
  });
213
211
  });
214
212
  /**
215
- * 묶음 머리가 있는 표.
213
+ * Grouped headers.
216
214
  *
217
- * 머리가 두 줄이 되고, 묶이지 않은 열은 두 줄에 걸친다(`.span-both`). 배치가 CSS grid 의
218
- * `grid-row` / `grid-column` 으로 이루어지므로 **줄 감싸개가 가장 위험한 자리**다.
215
+ * The header becomes two rows and ungrouped columns span both (`.span-both`). Placement runs
216
+ * through CSS `grid-row` / `grid-column`, which makes this the riskiest spot for a row wrapper.
219
217
  */
220
- describe('묶음 머리 — 머리가 두 줄이다', () => {
218
+ describe('grouped header spans two rows', () => {
221
219
  const columns = [
222
220
  { type: 'string', name: 'palletId', header: '파렛트 번호' },
223
221
  { type: 'string', name: 'width', header: { renderer: '폭', group: '치수' } },
@@ -237,55 +235,55 @@ describe('묶음 머리 — 머리가 두 줄이다', () => {
237
235
  await body.updateComplete;
238
236
  return { el, header, body };
239
237
  }
240
- it('머리가 두 줄로 세어진다 — 본문은 3번째 줄부터', async () => {
238
+ it('counts two header rows so the body starts at row 3', async () => {
241
239
  const { el, header, body } = await grouped();
242
240
  expect(header.renderRoot.querySelectorAll('[role=row]').length).to.equal(2);
243
- /* 머리 두 줄 + 1건 */
241
+ /* Two header rows plus one record. */
244
242
  expect(el.renderRoot.querySelector('[role=grid]').getAttribute('aria-rowcount')).to.equal('3');
245
243
  const rows = Array.from(body.renderRoot.querySelectorAll('[role=row]'));
246
244
  expect(rows[0].getAttribute('aria-rowindex')).to.equal('3');
247
245
  });
248
- it('묶음 머리가 몇 열을 덮는지 말한다', async () => {
246
+ it('says how many columns a group covers', async () => {
249
247
  const { header } = await grouped();
250
248
  const cells = Array.from(header.renderRoot.querySelectorAll('[role=columnheader]'));
251
249
  const first = cells[0];
252
250
  const group = cells.find(cell => cell.getAttribute('aria-colspan'));
253
- /* 묶이지 않은 열은 두 줄에 걸친다 */
251
+ /* An ungrouped column spans both header rows. */
254
252
  expect(first.getAttribute('aria-rowspan')).to.equal('2');
255
253
  expect(first.getAttribute('aria-colindex')).to.equal('1');
256
- /* 치수 묶음은 2 · 3열을 덮는다 */
257
- expect(group, '묶음 머리를 찾지 못했다').to.exist;
254
+ /* The group header covers columns 2 and 3. */
255
+ expect(group, 'found no group header').to.exist;
258
256
  expect(group.getAttribute('aria-colindex')).to.equal('2');
259
257
  expect(group.getAttribute('aria-colspan')).to.equal('2');
260
258
  });
261
- it('두 줄짜리 머리의 배치가 그대로다 — 묶음이 아래 줄보다 위에 있다', async () => {
259
+ it('keeps two-row header placement — group sits above its leaf columns', async () => {
262
260
  const { header } = await grouped();
263
261
  const cells = Array.from(header.renderRoot.querySelectorAll('[role=columnheader]'));
264
262
  const group = cells.find(cell => cell.getAttribute('aria-colspan'));
265
263
  const leaf = cells.find(cell => !cell.getAttribute('aria-colspan') && cell !== cells[0]);
266
- expect(leaf.offsetTop, '묶음 머리와 그 아래 열이 같은 높이에 있다').to.be.greaterThan(group.offsetTop);
267
- expect(group.offsetWidth, '묶음 머리가 두 열을 덮지 않는다').to.be.greaterThan(leaf.offsetWidth);
264
+ expect(leaf.offsetTop, 'group header and its leaf column at the same height').to.be.greaterThan(group.offsetTop);
265
+ expect(group.offsetWidth, 'group header does not cover two columns').to.be.greaterThan(leaf.offsetWidth);
268
266
  });
269
267
  });
270
268
  /**
271
- * 표 감싸개가 바깥 배치를 건드리지 않나.
269
+ * The grid wrapper must not disturb the surrounding layout.
272
270
  *
273
- * `role=grid` 안에는 줄만 들어갈 수 있어서, 설정 단추 자리와 페이지 넘김은 감싸개 **밖**으로
274
- * 나왔다. 그러면서 두 가지가 이 구조에 매인다.
271
+ * `role=grid` admits only rows, so the settings-button slot and the pager moved outside the
272
+ * wrapper. Two things then depend on this structure:
275
273
  *
276
274
  * ```
277
- * 세로 배치 호스트는 flex column 이다. 감싸개가 상자를 만들면 머리와 몸이 그 안으로 들어가
278
- * flex 항목이 하나가 되고, 몸의 flex: 1 이 듣지 않아 높이가 무너진다
279
- * 형제 선택자 「머리에 올렸을 때 설정 단추를 보인다」가 인접 형제 선택자다. 설정 단추가 감싸개
280
- * 바로 뒤에 있어야 그 규칙이 성립한다
275
+ * column layout the host is a flex column. A wrapper that generates a box swallows header
276
+ * and body into one flex item, and the body's flex: 1 stops working
277
+ * sibling selector "show the settings button on header hover" is an adjacent-sibling rule, so
278
+ * the button has to sit directly after the wrapper
281
279
  * ```
282
280
  */
283
- describe('표 감싸개는 배치에 끼어들지 않는다', () => {
281
+ describe('grid wrapper stays out of the layout', () => {
284
282
  const columns = [
285
283
  { type: 'string', name: 'palletId', header: '파렛트 번호' },
286
284
  { type: 'string', name: 'location', header: '보관 위치' }
287
285
  ].map(buildColumn);
288
- it('설정 단추는 표 바로 뒤에 있다 — 표 안이 아니라', async () => {
286
+ it('places the settings button after the grid, not inside it', async () => {
289
287
  const el = await fixture(html `
290
288
  <ox-grid
291
289
  .config=${{ ...ZERO_CONFIG, columns, rows: { ...ZERO_CONFIG.rows } }}
@@ -295,11 +293,11 @@ describe('표 감싸개는 배치에 끼어들지 않는다', () => {
295
293
  await el.updateComplete;
296
294
  const grid = el.renderRoot.querySelector('[role=grid]');
297
295
  const setting = el.renderRoot.querySelector('div[setting]');
298
- expect(setting, '설정 단추 자리가 없다').to.exist;
299
- expect(grid.contains(setting), '설정 단추가 표 안에 들어갔다 — 표 읽기가 깨진다').to.be.false;
300
- expect(grid.nextElementSibling, '인접 형제 규칙이 성립하지 않는다').to.equal(setting);
296
+ expect(setting, 'no settings slot').to.exist;
297
+ expect(grid.contains(setting), 'settings button inside the grid breaks table reading').to.be.false;
298
+ expect(grid.nextElementSibling, 'adjacent-sibling rule no longer holds').to.equal(setting);
301
299
  });
302
- it('머리와 몸이 그대로 호스트의 세로 배치를 쓴다', async () => {
300
+ it('leaves header and body as flex items of the host', async () => {
303
301
  const el = await fixture(html `
304
302
  <div style="height: 300px; display: flex">
305
303
  <ox-grid
@@ -315,9 +313,9 @@ describe('표 감싸개는 배치에 끼어들지 않는다', () => {
315
313
  const header = grid.renderRoot.querySelector('ox-grid-header');
316
314
  const body = grid.renderRoot.querySelector('ox-grid-body');
317
315
  expect(getComputedStyle(wrapper).display).to.equal('contents');
318
- /* 몸이 남는 높이를 다 쓴다 — 감싸개가 상자를 만들면 이 값이 머리 높이만큼으로 줄어든다 */
319
- expect(body.offsetHeight, '몸이 남는 높이를 쓰지 못한다').to.be.greaterThan(100);
320
- expect(body.offsetTop, '몸이 머리 아래에 있지 않다').to.be.greaterThan(0);
316
+ /* The body takes the remaining height; a wrapper with a box shrinks it to header height. */
317
+ expect(body.offsetHeight, 'body does not take the remaining height').to.be.greaterThan(100);
318
+ expect(body.offsetTop, 'body is not below the header').to.be.greaterThan(0);
321
319
  expect(header.offsetTop).to.equal(0);
322
320
  });
323
321
  });
@@ -1 +1 @@
1
- {"version":3,"file":"accessible-name.test.js","sourceRoot":"","sources":["../../test/accessible-name.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { expect, fixture } from '@open-wc/testing'\nimport { html } from 'lit'\n\nimport '../src/data-grid/data-grid.js'\nimport '../src/data-grid/data-grid-body.js'\nimport { buildColumn } from '../src/configure/column-builder.js'\nimport { columnText } from '../src/utils/column-text.js'\nimport { ZERO_CONFIG, ZERO_DATA } from '../src/configure/zero-config.js'\n\n/**\n * 입력칸의 이름.\n *\n * ## 왜 시험이 필요한가\n *\n * 이 grid 안에는 이름 없는 입력칸이 여럿 있었다 — 줄을 고르는 체크박스, 머리줄의 filter 상자,\n * 그리고 포커스를 담고 있는 IME buffer. 눈으로 보는 사람에게는 **자리**가 뜻을 말해 준다(맨 왼쪽\n * 좁은 칸이면 줄 선택, 머리줄 이름 바로 아래면 그 column 의 filter). 그런데 자리는 그림일 뿐이고\n * 관계가 아니라서, 화면을 못 보는 사람에게는 이름 없는 상자들만 남는다.\n *\n * ## 무엇을 지키려는가\n *\n * 이름의 출처가 하나여야 한다. `header` 를 고쳤을 때 읽히는 이름도 함께 바뀌어야 하고, 그러려면\n * 부르는 쪽이 이름을 다시 적어 두면 안 된다. `columnText()` 가 그 유일한 출처다.\n *\n * `buildColumn` 이 문자열 header 를 `() => header` 함수로 바꿔 버리는 자리가 있었는데, 그 함수가\n * 돌려주는 것은 lit template 이라 이름을 되꺼낼 수 없다 — 여기서 이름이 사라지고 있었다.\n */\n\ndescribe('column 이름의 출처는 하나다', () => {\n it('문자열로 준 header 는 함수로 바뀌어도 글자가 남는다', () => {\n const column = buildColumn({ type: 'string', name: 'palletId', header: '파렛트 번호' })\n\n expect(columnText(column)).to.equal('파렛트 번호')\n })\n\n it('header.renderer 에 문자열을 준 경우도 같다', () => {\n const column = buildColumn({ type: 'string', name: 'palletId', header: { renderer: '파렛트 번호' } })\n\n expect(columnText(column)).to.equal('파렛트 번호')\n })\n\n it('label: true 는 header 의 이름을 물려받는다', () => {\n const column = buildColumn({ type: 'string', name: 'palletId', header: '파렛트 번호', label: true })\n\n expect(columnText(column)).to.equal('파렛트 번호')\n })\n\n it('header 를 함수로 그리면 이름을 되꺼낼 수 없다 — 그때는 column 이름으로 물러난다', () => {\n const column = buildColumn({ type: 'string', name: 'palletId', header: { renderer: () => html`<i>x</i>` } })\n\n expect(columnText(column)).to.equal('palletId')\n })\n\n it('column 이 없으면 이름을 지어내지 않는다', () => {\n expect(columnText(undefined)).to.equal('')\n expect(columnText(null)).to.equal('')\n })\n})\n\ndescribe('줄을 고르는 칸에는 이름이 있다', () => {\n const columns = [\n { type: 'gutter', gutterName: 'row-selector', name: '__selected__', multiple: true },\n { type: 'string', name: 'palletId', header: '파렛트 번호' }\n ].map(buildColumn)\n\n it('행마다 놓인 체크박스가 몇 번째 줄인지 말한다', async () => {\n const body: any = await fixture(html`\n <ox-grid-body\n .config=${{ ...ZERO_CONFIG, columns }}\n .columns=${columns}\n .data=${{ ...ZERO_DATA, records: [{ palletId: 'P-0001' }, { palletId: 'P-0002' }] }}\n ></ox-grid-body>\n `)\n\n await body.updateComplete\n\n /* 체크박스는 각 칸(ox-grid-field)의 shadow root 안에 그려진다 — 한 겹 더 들어가야 보인다. */\n const fields = Array.from(body.renderRoot.querySelectorAll('ox-grid-field')) as any[]\n\n await Promise.all(fields.map(field => field.updateComplete))\n\n const boxes = fields.flatMap(field =>\n Array.from(field.renderRoot?.querySelectorAll('input[type=checkbox], input[type=radio]') || [])\n ) as HTMLInputElement[]\n\n expect(boxes.length, '줄을 고르는 칸을 찾지 못했다').to.be.greaterThan(0)\n boxes.forEach(box =>\n expect(box.getAttribute('aria-label'), '이름 없는 체크박스가 남아 있다').to.be.a('string').and.not.equal('')\n )\n })\n})\n\ndescribe('포커스를 담고 있는 IME buffer 가 지금 선 칸을 말한다', () => {\n const columns = [\n { type: 'string', name: 'palletId', header: '파렛트 번호' },\n { type: 'string', name: 'location', header: '보관 위치' }\n ].map(buildColumn)\n\n async function bodyFocusedAt(column: number) {\n const body: any = await fixture(html`\n <ox-grid-body\n .config=${{ ...ZERO_CONFIG, columns }}\n .columns=${columns}\n .data=${{ ...ZERO_DATA, records: [{ palletId: 'P-0001', location: 'A-01' }] }}\n .focused=${{ row: 0, column }}\n ></ox-grid-body>\n `)\n\n await body.updateComplete\n\n return body.renderRoot.querySelector('#ime-buffer') as HTMLInputElement\n }\n\n it('첫 칸에 서 있으면 첫 칸의 이름을 읽는다', async () => {\n expect((await bodyFocusedAt(0)).getAttribute('aria-label')).to.contain('파렛트 번호')\n })\n\n it('옆 칸으로 옮기면 읽히는 이름도 바뀐다 — 안 바뀌면 어디에 선 것인지 알 수 없다', async () => {\n expect((await bodyFocusedAt(1)).getAttribute('aria-label')).to.contain('보관 위치')\n })\n\n it('보조기술에서 지우지 않는다 — 포커스를 받는 자리다', async () => {\n expect((await bodyFocusedAt(0)).getAttribute('aria-hidden')).to.be.null\n })\n})\n\n/**\n * 표를 표라고 선언했나.\n *\n * ## 왜 시험이 필요한가\n *\n * 이 표는 CSS grid 로 그린다 — 칸이 평평하게 놓여 있고 **줄이라는 요소가 없다.** 보기에는 줄이\n * 있지만 그것은 같은 높이에 놓였다는 그림일 뿐이다. 그래서 스크린 리더에게 이 화면은 「여기가\n * 표다」도, 「지금 3행 3열이다」도, 「전체는 40행이다」도 아닌 **입력칸 더미**였다.\n *\n * 줄 감싸개를 넣으면서 두 가지가 동시에 깨질 수 있다.\n *\n * ```\n * 뜻이 서나 role=grid / row / columnheader / gridcell 이 제대로 겹쳐 있나\n * 모양이 사나 display: contents 가 정말로 상자를 안 만드나 — 만들면 표가 한 줄로 무너진다\n * ```\n *\n * 두 번째가 특히 중요하다. `display: contents` 를 잘못 걸면 칸들이 grid 항목이 아니게 되어\n * 열이 사라진다. 그래서 **자리를 실제로 확인한다** — 한 줄 안의 칸은 왼쪽으로 갈수록 x 가 커지고,\n * 줄과 줄은 y 가 달라야 한다.\n */\ndescribe('표를 표라고 선언한다', () => {\n /* ox-grid 는 이미 다듬어진 column 을 받는다 — 다듬는 것은 ox-grist 의 일이다. */\n const columns = [\n { type: 'gutter', gutterName: 'row-selector', name: '__selected__', multiple: true },\n { type: 'string', name: 'palletId', header: '파렛트 번호', sortable: true },\n { type: 'string', name: 'location', header: '보관 위치', sortable: true }\n ].map(buildColumn)\n\n async function grid(overrides: any = {}) {\n const el: any = await fixture(html`\n <ox-grid\n aria-label=\"입고 대기 목록\"\n .config=${{ ...ZERO_CONFIG, columns, rows: { ...ZERO_CONFIG.rows } }}\n .data=${{\n page: 1,\n limit: 20,\n total: 40,\n records: [\n { palletId: 'P-0001', location: 'A-01' },\n { palletId: 'P-0002', location: 'A-02' }\n ],\n ...overrides\n }}\n ></ox-grid>\n `)\n\n await el.updateComplete\n\n const header = el.renderRoot.querySelector('ox-grid-header')\n const body = el.renderRoot.querySelector('ox-grid-body')\n\n await header?.updateComplete\n await body?.updateComplete\n\n return { el, header, body }\n }\n\n it('표 · 줄 · 칸이 겹쳐 있다', async () => {\n const { el, header, body } = await grid()\n\n const grid_ = el.renderRoot.querySelector('[role=grid]')\n\n expect(grid_, 'role=grid 가 없다').to.exist\n expect(header.getAttribute('role')).to.equal('rowgroup')\n expect(body.getAttribute('role')).to.equal('rowgroup')\n\n expect(header.renderRoot.querySelectorAll('[role=row]').length).to.equal(1)\n expect(body.renderRoot.querySelectorAll('[role=row]').length).to.equal(2)\n expect(header.renderRoot.querySelectorAll('[role=columnheader]').length).to.equal(3)\n })\n\n it('전체가 몇 줄 몇 열인지 말한다 — 지금 페이지가 아니라', async () => {\n const { el } = await grid()\n\n const grid_ = el.renderRoot.querySelector('[role=grid]')\n\n /* 머리 한 줄 + 전체 40건. 화면에 두 줄만 그려도 40건짜리 표다. */\n expect(grid_.getAttribute('aria-rowcount')).to.equal('41')\n expect(grid_.getAttribute('aria-colcount')).to.equal('3')\n expect(grid_.getAttribute('aria-label')).to.equal('입고 대기 목록')\n })\n\n it('2페이지의 첫 줄은 21번째 줄이다 — 페이지마다 1번으로 돌아가지 않는다', async () => {\n const { body } = await grid({ page: 2 })\n\n const rows = Array.from(body.renderRoot.querySelectorAll('[role=row]')) as HTMLElement[]\n\n /* 머리 한 줄(1) + 앞 페이지 20건 = 22번째부터 */\n expect(rows[0].getAttribute('aria-rowindex')).to.equal('22')\n expect(rows[1].getAttribute('aria-rowindex')).to.equal('23')\n })\n\n it('칸이 몇 번째 열인지 말한다 — 남는 폭을 메우는 칸은 세지 않는다', async () => {\n const { body } = await grid()\n\n const row = body.renderRoot.querySelector('[role=row]') as HTMLElement\n const cells = Array.from(row.querySelectorAll('[role=gridcell]')) as HTMLElement[]\n\n expect(cells.map(cell => cell.getAttribute('aria-colindex'))).to.deep.equal(['1', '2', '3'])\n expect(row.querySelectorAll('[role=presentation]').length, '메우는 칸이 표의 칸으로 세어졌다').to.equal(1)\n })\n\n it('정렬 상태를 글자로도 말한다 — 화살표는 눈으로만 읽힌다', async () => {\n const el: any = await fixture(html`\n <ox-grid\n .config=${{ ...ZERO_CONFIG, columns, rows: { ...ZERO_CONFIG.rows } }}\n .sorters=${[{ name: 'palletId', desc: true }]}\n .data=${{ records: [{ palletId: 'P-0001', location: 'A-01' }] }}\n ></ox-grid>\n `)\n\n await el.updateComplete\n\n const header = el.renderRoot.querySelector('ox-grid-header')\n await header.updateComplete\n\n const headers = Array.from(header.renderRoot.querySelectorAll('[role=columnheader]')) as HTMLElement[]\n const sorts = headers.map(cell => cell.getAttribute('aria-sort'))\n\n /* 거터는 정렬할 수 없다 — 있지도 않은 기능을 있다고 말하지 않는다. */\n expect(sorts[0]).to.be.null\n expect(sorts[1], '내림차순으로 정렬된 열').to.equal('descending')\n expect(sorts[2], '정렬할 수 있으나 지금은 아닌 열').to.equal('none')\n })\n\n it('줄 감싸개가 배치를 건드리지 않는다 — 상자를 만들면 표가 무너진다', async () => {\n const { body } = await grid()\n\n const rows = Array.from(body.renderRoot.querySelectorAll('[role=row]')) as HTMLElement[]\n const first = Array.from(rows[0].children) as HTMLElement[]\n\n /* 감싸개 자신은 상자가 없다 */\n expect(getComputedStyle(rows[0]).display).to.equal('contents')\n\n /* 한 줄 안의 칸은 오른쪽으로 나아간다 — 한 자리에 겹쳐 있으면 열이 사라진 것이다 */\n const lefts = first.map(cell => cell.offsetLeft)\n expect(new Set(lefts).size, '칸들이 같은 x 에 겹쳐 있다').to.be.greaterThan(1)\n lefts.slice(1).forEach((left, index) => expect(left).to.be.greaterThan(lefts[index]))\n\n /* 줄과 줄은 아래로 내려간다 */\n const firstTop = (rows[0].children[0] as HTMLElement).offsetTop\n const secondTop = (rows[1].children[0] as HTMLElement).offsetTop\n expect(secondTop, '두 줄이 같은 y 에 있다').to.be.greaterThan(firstTop)\n })\n})\n\n/**\n * 묶음 머리가 있는 표.\n *\n * 머리가 두 줄이 되고, 묶이지 않은 열은 두 줄에 걸친다(`.span-both`). 배치가 CSS grid 의\n * `grid-row` / `grid-column` 으로 이루어지므로 **줄 감싸개가 가장 위험한 자리**다.\n */\ndescribe('묶음 머리 — 머리가 두 줄이다', () => {\n const columns = [\n { type: 'string', name: 'palletId', header: '파렛트 번호' },\n { type: 'string', name: 'width', header: { renderer: '폭', group: '치수' } },\n { type: 'string', name: 'height', header: { renderer: '높이', group: '치수' } }\n ].map(buildColumn)\n\n async function grouped() {\n const el: any = await fixture(html`\n <ox-grid\n .config=${{ ...ZERO_CONFIG, columns, rows: { ...ZERO_CONFIG.rows } }}\n .data=${{ total: 1, records: [{ palletId: 'P-0001', width: '1200', height: '900' }] }}\n ></ox-grid>\n `)\n\n await el.updateComplete\n\n const header = el.renderRoot.querySelector('ox-grid-header')\n const body = el.renderRoot.querySelector('ox-grid-body')\n\n await header.updateComplete\n await body.updateComplete\n\n return { el, header, body }\n }\n\n it('머리가 두 줄로 세어진다 — 본문은 3번째 줄부터', async () => {\n const { el, header, body } = await grouped()\n\n expect(header.renderRoot.querySelectorAll('[role=row]').length).to.equal(2)\n\n /* 머리 두 줄 + 1건 */\n expect(el.renderRoot.querySelector('[role=grid]').getAttribute('aria-rowcount')).to.equal('3')\n\n const rows = Array.from(body.renderRoot.querySelectorAll('[role=row]')) as HTMLElement[]\n expect(rows[0].getAttribute('aria-rowindex')).to.equal('3')\n })\n\n it('묶음 머리가 몇 열을 덮는지 말한다', async () => {\n const { header } = await grouped()\n\n const cells = Array.from(header.renderRoot.querySelectorAll('[role=columnheader]')) as HTMLElement[]\n const first = cells[0]\n const group = cells.find(cell => cell.getAttribute('aria-colspan'))\n\n /* 묶이지 않은 열은 두 줄에 걸친다 */\n expect(first.getAttribute('aria-rowspan')).to.equal('2')\n expect(first.getAttribute('aria-colindex')).to.equal('1')\n\n /* 치수 묶음은 2 · 3열을 덮는다 */\n expect(group, '묶음 머리를 찾지 못했다').to.exist\n expect(group!.getAttribute('aria-colindex')).to.equal('2')\n expect(group!.getAttribute('aria-colspan')).to.equal('2')\n })\n\n it('두 줄짜리 머리의 배치가 그대로다 — 묶음이 아래 줄보다 위에 있다', async () => {\n const { header } = await grouped()\n\n const cells = Array.from(header.renderRoot.querySelectorAll('[role=columnheader]')) as HTMLElement[]\n const group = cells.find(cell => cell.getAttribute('aria-colspan'))!\n const leaf = cells.find(cell => !cell.getAttribute('aria-colspan') && cell !== cells[0])!\n\n expect(leaf.offsetTop, '묶음 머리와 그 아래 열이 같은 높이에 있다').to.be.greaterThan(group.offsetTop)\n expect(group.offsetWidth, '묶음 머리가 두 열을 덮지 않는다').to.be.greaterThan(leaf.offsetWidth)\n })\n})\n\n/**\n * 표 감싸개가 바깥 배치를 건드리지 않나.\n *\n * `role=grid` 안에는 줄만 들어갈 수 있어서, 설정 단추 자리와 페이지 넘김은 감싸개 **밖**으로\n * 나왔다. 그러면서 두 가지가 이 구조에 매인다.\n *\n * ```\n * 세로 배치 호스트는 flex column 이다. 감싸개가 상자를 만들면 머리와 몸이 그 안으로 들어가\n * flex 항목이 하나가 되고, 몸의 flex: 1 이 듣지 않아 높이가 무너진다\n * 형제 선택자 「머리에 올렸을 때 설정 단추를 보인다」가 인접 형제 선택자다. 설정 단추가 감싸개\n * 바로 뒤에 있어야 그 규칙이 성립한다\n * ```\n */\ndescribe('표 감싸개는 배치에 끼어들지 않는다', () => {\n const columns = [\n { type: 'string', name: 'palletId', header: '파렛트 번호' },\n { type: 'string', name: 'location', header: '보관 위치' }\n ].map(buildColumn)\n\n it('설정 단추는 표 바로 뒤에 있다 — 표 안이 아니라', async () => {\n const el: any = await fixture(html`\n <ox-grid\n .config=${{ ...ZERO_CONFIG, columns, rows: { ...ZERO_CONFIG.rows } }}\n .data=${{ total: 1, records: [{ palletId: 'P-0001', location: 'A-01' }] }}\n ></ox-grid>\n `)\n\n await el.updateComplete\n\n const grid = el.renderRoot.querySelector('[role=grid]') as HTMLElement\n const setting = el.renderRoot.querySelector('div[setting]') as HTMLElement\n\n expect(setting, '설정 단추 자리가 없다').to.exist\n expect(grid.contains(setting), '설정 단추가 표 안에 들어갔다 — 표 읽기가 깨진다').to.be.false\n expect(grid.nextElementSibling, '인접 형제 규칙이 성립하지 않는다').to.equal(setting)\n })\n\n it('머리와 몸이 그대로 호스트의 세로 배치를 쓴다', async () => {\n const el: any = await fixture(html`\n <div style=\"height: 300px; display: flex\">\n <ox-grid\n style=\"flex: 1\"\n .config=${{ ...ZERO_CONFIG, columns, rows: { ...ZERO_CONFIG.rows } }}\n .data=${{ total: 1, records: [{ palletId: 'P-0001', location: 'A-01' }] }}\n ></ox-grid>\n </div>\n `)\n\n const grid = el.querySelector('ox-grid') as any\n await grid.updateComplete\n\n const wrapper = grid.renderRoot.querySelector('[role=grid]') as HTMLElement\n const header = grid.renderRoot.querySelector('ox-grid-header') as HTMLElement\n const body = grid.renderRoot.querySelector('ox-grid-body') as HTMLElement\n\n expect(getComputedStyle(wrapper).display).to.equal('contents')\n\n /* 몸이 남는 높이를 다 쓴다 — 감싸개가 상자를 만들면 이 값이 머리 높이만큼으로 줄어든다 */\n expect(body.offsetHeight, '몸이 남는 높이를 쓰지 못한다').to.be.greaterThan(100)\n expect(body.offsetTop, '몸이 머리 아래에 있지 않다').to.be.greaterThan(0)\n expect(header.offsetTop).to.equal(0)\n })\n})\n"]}
1
+ {"version":3,"file":"accessible-name.test.js","sourceRoot":"","sources":["../../test/accessible-name.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAA;AAClD,OAAO,EAAE,IAAI,EAAE,MAAM,KAAK,CAAA;AAE1B,OAAO,+BAA+B,CAAA;AACtC,OAAO,oCAAoC,CAAA;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,oCAAoC,CAAA;AAChE,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAA;AACxD,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,iCAAiC,CAAA;AAExE;;;;;;;;;;;;;;;;;GAiBG;AAEH,QAAQ,CAAC,4BAA4B,EAAE,GAAG,EAAE;IAC1C,EAAE,CAAC,+DAA+D,EAAE,GAAG,EAAE;QACvE,MAAM,MAAM,GAAG,WAAW,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAA;QAElF,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAA;IAC/C,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,qDAAqD,EAAE,GAAG,EAAE;QAC7D,MAAM,MAAM,GAAG,WAAW,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAA;QAEhG,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAA;IAC/C,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,0CAA0C,EAAE,GAAG,EAAE;QAClD,MAAM,MAAM,GAAG,WAAW,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAA;QAE/F,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAA;IAC/C,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,kEAAkE,EAAE,GAAG,EAAE;QAC1E,MAAM,MAAM,GAAG,WAAW,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC,IAAI,CAAA,UAAU,EAAE,EAAE,CAAC,CAAA;QAE5G,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,CAAA;IACjD,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,yCAAyC,EAAE,GAAG,EAAE;QACjD,MAAM,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;QAC1C,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;IACvC,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA;AAEF,QAAQ,CAAC,4BAA4B,EAAE,GAAG,EAAE;IAC1C,MAAM,OAAO,GAAG;QACd,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,cAAc,EAAE,IAAI,EAAE,cAAc,EAAE,QAAQ,EAAE,IAAI,EAAE;QACpF,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE;KACvD,CAAC,GAAG,CAAC,WAAW,CAAC,CAAA;IAElB,EAAE,CAAC,sCAAsC,EAAE,KAAK,IAAI,EAAE;QACpD,MAAM,IAAI,GAAQ,MAAM,OAAO,CAAC,IAAI,CAAA;;kBAEtB,EAAE,GAAG,WAAW,EAAE,OAAO,EAAE;mBAC1B,OAAO;gBACV,EAAE,GAAG,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,EAAE;;KAEtF,CAAC,CAAA;QAEF,MAAM,IAAI,CAAC,cAAc,CAAA;QAEzB,4EAA4E;QAC5E,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,gBAAgB,CAAC,eAAe,CAAC,CAAU,CAAA;QAErF,MAAM,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,CAAA;QAE5D,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,WACnC,OAAA,KAAK,CAAC,IAAI,CAAC,CAAA,MAAA,KAAK,CAAC,UAAU,0CAAE,gBAAgB,CAAC,yCAAyC,CAAC,KAAI,EAAE,CAAC,CAAA,EAAA,CAC1E,CAAA;QAEvB,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,yBAAyB,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC,CAAA;QACpE,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAClB,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,YAAY,CAAC,EAAE,6BAA6B,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAC1G,CAAA;IACH,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA;AAEF,QAAQ,CAAC,4CAA4C,EAAE,GAAG,EAAE;IAC1D,MAAM,OAAO,GAAG;QACd,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE;QACtD,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE;KACtD,CAAC,GAAG,CAAC,WAAW,CAAC,CAAA;IAElB,KAAK,UAAU,aAAa,CAAC,MAAc;QACzC,MAAM,IAAI,GAAQ,MAAM,OAAO,CAAC,IAAI,CAAA;;kBAEtB,EAAE,GAAG,WAAW,EAAE,OAAO,EAAE;mBAC1B,OAAO;gBACV,EAAE,GAAG,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,EAAE;mBAClE,EAAE,GAAG,EAAE,CAAC,EAAE,MAAM,EAAE;;KAEhC,CAAC,CAAA;QAEF,MAAM,IAAI,CAAC,cAAc,CAAA;QAEzB,OAAO,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,aAAa,CAAqB,CAAA;IACzE,CAAC;IAED,EAAE,CAAC,+CAA+C,EAAE,KAAK,IAAI,EAAE;QAC7D,MAAM,CAAC,CAAC,MAAM,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAA;IAClF,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,sDAAsD,EAAE,KAAK,IAAI,EAAE;QACpE,MAAM,CAAC,CAAC,MAAM,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,CAAA;IACjF,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,2DAA2D,EAAE,KAAK,IAAI,EAAE;QACzE,MAAM,CAAC,CAAC,MAAM,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,IAAI,CAAA;IACzE,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA;AAEF;;;;;;;;;;;;;;;;;;GAkBG;AACH,QAAQ,CAAC,6BAA6B,EAAE,GAAG,EAAE;IAC3C,+EAA+E;IAC/E,MAAM,OAAO,GAAG;QACd,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,cAAc,EAAE,IAAI,EAAE,cAAc,EAAE,QAAQ,EAAE,IAAI,EAAE;QACpF,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE;QACtE,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE;KACtE,CAAC,GAAG,CAAC,WAAW,CAAC,CAAA;IAElB,KAAK,UAAU,IAAI,CAAC,YAAiB,EAAE;QACrC,MAAM,EAAE,GAAQ,MAAM,OAAO,CAAC,IAAI,CAAA;;;kBAGpB,EAAE,GAAG,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,GAAG,WAAW,CAAC,IAAI,EAAE,EAAE;gBAC5D;YACN,IAAI,EAAE,CAAC;YACP,KAAK,EAAE,EAAE;YACT,KAAK,EAAE,EAAE;YACT,OAAO,EAAE;gBACP,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE;gBACxC,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE;aACzC;YACD,GAAG,SAAS;SACb;;KAEJ,CAAC,CAAA;QAEF,MAAM,EAAE,CAAC,cAAc,CAAA;QAEvB,MAAM,MAAM,GAAG,EAAE,CAAC,UAAU,CAAC,aAAa,CAAC,gBAAgB,CAAC,CAAA;QAC5D,MAAM,IAAI,GAAG,EAAE,CAAC,UAAU,CAAC,aAAa,CAAC,cAAc,CAAC,CAAA;QAExD,MAAM,CAAA,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,cAAc,CAAA,CAAA;QAC5B,MAAM,CAAA,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,cAAc,CAAA,CAAA;QAE1B,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAA;IAC7B,CAAC;IAED,EAAE,CAAC,0BAA0B,EAAE,KAAK,IAAI,EAAE;QACxC,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,IAAI,EAAE,CAAA;QAEzC,MAAM,KAAK,GAAG,EAAE,CAAC,UAAU,CAAC,aAAa,CAAC,aAAa,CAAC,CAAA;QAExD,MAAM,CAAC,KAAK,EAAE,cAAc,CAAC,CAAC,EAAE,CAAC,KAAK,CAAA;QACtC,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,CAAA;QACxD,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,CAAA;QAEtD,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,gBAAgB,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;QAC3E,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,gBAAgB,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;QACzE,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,gBAAgB,CAAC,qBAAqB,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;IACtF,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,gDAAgD,EAAE,KAAK,IAAI,EAAE;QAC9D,MAAM,EAAE,EAAE,EAAE,GAAG,MAAM,IAAI,EAAE,CAAA;QAE3B,MAAM,KAAK,GAAG,EAAE,CAAC,UAAU,CAAC,aAAa,CAAC,aAAa,CAAC,CAAA;QAExD,6EAA6E;QAC7E,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;QAC1D,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;QACzD,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,CAAA;IAC/D,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,2CAA2C,EAAE,KAAK,IAAI,EAAE;QACzD,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,CAAA;QAExC,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,gBAAgB,CAAC,YAAY,CAAC,CAAkB,CAAA;QAExF,+CAA+C;QAC/C,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;QAC5D,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;IAC9D,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,uDAAuD,EAAE,KAAK,IAAI,EAAE;QACrE,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,IAAI,EAAE,CAAA;QAE7B,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,YAAY,CAAgB,CAAA;QACtE,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,gBAAgB,CAAC,iBAAiB,CAAC,CAAkB,CAAA;QAElF,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAA;QAC5F,MAAM,CAAC,GAAG,CAAC,gBAAgB,CAAC,qBAAqB,CAAC,CAAC,MAAM,EAAE,kCAAkC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;IAC5G,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,qDAAqD,EAAE,KAAK,IAAI,EAAE;QACnE,MAAM,EAAE,GAAQ,MAAM,OAAO,CAAC,IAAI,CAAA;;kBAEpB,EAAE,GAAG,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,GAAG,WAAW,CAAC,IAAI,EAAE,EAAE;mBACzD,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;gBACrC,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,EAAE;;KAElE,CAAC,CAAA;QAEF,MAAM,EAAE,CAAC,cAAc,CAAA;QAEvB,MAAM,MAAM,GAAG,EAAE,CAAC,UAAU,CAAC,aAAa,CAAC,gBAAgB,CAAC,CAAA;QAC5D,MAAM,MAAM,CAAC,cAAc,CAAA;QAE3B,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,gBAAgB,CAAC,qBAAqB,CAAC,CAAkB,CAAA;QACtG,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,WAAW,CAAC,CAAC,CAAA;QAEjE,0FAA0F;QAC1F,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,IAAI,CAAA;QAC3B,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,mBAAmB,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,CAAA;QAC5D,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,mCAAmC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;IACxE,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,0EAA0E,EAAE,KAAK,IAAI,EAAE;QACxF,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,IAAI,EAAE,CAAA;QAE7B,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,gBAAgB,CAAC,YAAY,CAAC,CAAkB,CAAA;QACxF,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAkB,CAAA;QAE3D,MAAM,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,CAAA;QAE9D,oEAAoE;QACpE,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;QAChD,MAAM,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,6BAA6B,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC,CAAA;QAC/E,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,WAAW,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;QAErF,qBAAqB;QACrB,MAAM,QAAQ,GAAI,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAiB,CAAC,SAAS,CAAA;QAC/D,MAAM,SAAS,GAAI,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAiB,CAAC,SAAS,CAAA;QAChE,MAAM,CAAC,SAAS,EAAE,wBAAwB,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAA;IACzE,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA;AAEF;;;;;GAKG;AACH,QAAQ,CAAC,+BAA+B,EAAE,GAAG,EAAE;IAC7C,MAAM,OAAO,GAAG;QACd,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE;QACtD,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;QACzE,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;KAC5E,CAAC,GAAG,CAAC,WAAW,CAAC,CAAA;IAElB,KAAK,UAAU,OAAO;QACpB,MAAM,EAAE,GAAQ,MAAM,OAAO,CAAC,IAAI,CAAA;;kBAEpB,EAAE,GAAG,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,GAAG,WAAW,CAAC,IAAI,EAAE,EAAE;gBAC5D,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE;;KAExF,CAAC,CAAA;QAEF,MAAM,EAAE,CAAC,cAAc,CAAA;QAEvB,MAAM,MAAM,GAAG,EAAE,CAAC,UAAU,CAAC,aAAa,CAAC,gBAAgB,CAAC,CAAA;QAC5D,MAAM,IAAI,GAAG,EAAE,CAAC,UAAU,CAAC,aAAa,CAAC,cAAc,CAAC,CAAA;QAExD,MAAM,MAAM,CAAC,cAAc,CAAA;QAC3B,MAAM,IAAI,CAAC,cAAc,CAAA;QAEzB,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAA;IAC7B,CAAC;IAED,EAAE,CAAC,oDAAoD,EAAE,KAAK,IAAI,EAAE;QAClE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,OAAO,EAAE,CAAA;QAE5C,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,gBAAgB,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;QAE3E,sCAAsC;QACtC,MAAM,CAAC,EAAE,CAAC,UAAU,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC,YAAY,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;QAE9F,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,gBAAgB,CAAC,YAAY,CAAC,CAAkB,CAAA;QACxF,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IAC7D,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,sCAAsC,EAAE,KAAK,IAAI,EAAE;QACpD,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,OAAO,EAAE,CAAA;QAElC,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,gBAAgB,CAAC,qBAAqB,CAAC,CAAkB,CAAA;QACpG,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAA;QACtB,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,cAAc,CAAC,CAAC,CAAA;QAEnE,iDAAiD;QACjD,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;QACxD,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;QAEzD,8CAA8C;QAC9C,MAAM,CAAC,KAAK,EAAE,uBAAuB,CAAC,CAAC,EAAE,CAAC,KAAK,CAAA;QAC/C,MAAM,CAAC,KAAM,CAAC,YAAY,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;QAC1D,MAAM,CAAC,KAAM,CAAC,YAAY,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IAC3D,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,oEAAoE,EAAE,KAAK,IAAI,EAAE;QAClF,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,OAAO,EAAE,CAAA;QAElC,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,gBAAgB,CAAC,qBAAqB,CAAC,CAAkB,CAAA;QACpG,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,cAAc,CAAC,CAAE,CAAA;QACpE,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,cAAc,CAAC,IAAI,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,CAAE,CAAA;QAEzF,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,qDAAqD,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,WAAW,CAAC,KAAK,CAAC,SAAS,CAAC,CAAA;QAChH,MAAM,CAAC,KAAK,CAAC,WAAW,EAAE,yCAAyC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;IAC1G,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA;AAEF;;;;;;;;;;;;GAYG;AACH,QAAQ,CAAC,sCAAsC,EAAE,GAAG,EAAE;IACpD,MAAM,OAAO,GAAG;QACd,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE;QACtD,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE;KACtD,CAAC,GAAG,CAAC,WAAW,CAAC,CAAA;IAElB,EAAE,CAAC,0DAA0D,EAAE,KAAK,IAAI,EAAE;QACxE,MAAM,EAAE,GAAQ,MAAM,OAAO,CAAC,IAAI,CAAA;;kBAEpB,EAAE,GAAG,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,GAAG,WAAW,CAAC,IAAI,EAAE,EAAE;gBAC5D,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,EAAE;;KAE5E,CAAC,CAAA;QAEF,MAAM,EAAE,CAAC,cAAc,CAAA;QAEvB,MAAM,IAAI,GAAG,EAAE,CAAC,UAAU,CAAC,aAAa,CAAC,aAAa,CAAgB,CAAA;QACtE,MAAM,OAAO,GAAG,EAAE,CAAC,UAAU,CAAC,aAAa,CAAC,cAAc,CAAgB,CAAA;QAE1E,MAAM,CAAC,OAAO,EAAE,kBAAkB,CAAC,CAAC,EAAE,CAAC,KAAK,CAAA;QAC5C,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,sDAAsD,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,CAAA;QAClG,MAAM,CAAC,IAAI,CAAC,kBAAkB,EAAE,uCAAuC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;IAC5F,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,kDAAkD,EAAE,KAAK,IAAI,EAAE;QAChE,MAAM,EAAE,GAAQ,MAAM,OAAO,CAAC,IAAI,CAAA;;;;oBAIlB,EAAE,GAAG,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,GAAG,WAAW,CAAC,IAAI,EAAE,EAAE;kBAC5D,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,EAAE;;;KAG9E,CAAC,CAAA;QAEF,MAAM,IAAI,GAAG,EAAE,CAAC,aAAa,CAAC,SAAS,CAAQ,CAAA;QAC/C,MAAM,IAAI,CAAC,cAAc,CAAA;QAEzB,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,aAAa,CAAgB,CAAA;QAC3E,MAAM,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,gBAAgB,CAAgB,CAAA;QAC7E,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,cAAc,CAAgB,CAAA;QAEzE,MAAM,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,CAAA;QAE9D,4FAA4F;QAC5F,MAAM,CAAC,IAAI,CAAC,YAAY,EAAE,yCAAyC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,CAAA;QAC3F,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,8BAA8B,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC,CAAA;QAC3E,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;IACtC,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA","sourcesContent":["import { expect, fixture } from '@open-wc/testing'\nimport { html } from 'lit'\n\nimport '../src/data-grid/data-grid.js'\nimport '../src/data-grid/data-grid-body.js'\nimport { buildColumn } from '../src/configure/column-builder.js'\nimport { columnText } from '../src/utils/column-text.js'\nimport { ZERO_CONFIG, ZERO_DATA } from '../src/configure/zero-config.js'\n\n/**\n * Accessible names on the inputs inside the grid.\n *\n * ## Why this is tested\n *\n * Several inputs here had no name: the row-select checkboxes, the header filter boxes, and the IME\n * buffer that holds focus. Position carries the meaning for a sighted reader — a narrow leftmost\n * column means row select, a box under a header name filters that column — but position is a\n * picture, not a relationship, so nothing of it reaches a screen reader.\n *\n * ## What has to hold\n *\n * The name has one source. Editing `header` must change what is announced, which means callers\n * cannot spell the name out again. `columnText()` is that source.\n *\n * `buildColumn` turns a string header into `() => header`, and that function returns a lit template\n * the name cannot be read back out of. The name was disappearing right there.\n */\n\ndescribe('column name has one source', () => {\n it('keeps the plain text after a string header becomes a function', () => {\n const column = buildColumn({ type: 'string', name: 'palletId', header: '파렛트 번호' })\n\n expect(columnText(column)).to.equal('파렛트 번호')\n })\n\n it('keeps it when the string arrives as header.renderer', () => {\n const column = buildColumn({ type: 'string', name: 'palletId', header: { renderer: '파렛트 번호' } })\n\n expect(columnText(column)).to.equal('파렛트 번호')\n })\n\n it('lets label: true inherit the header name', () => {\n const column = buildColumn({ type: 'string', name: 'palletId', header: '파렛트 번호', label: true })\n\n expect(columnText(column)).to.equal('파렛트 번호')\n })\n\n it('falls back to column.name when the header is drawn by a function', () => {\n const column = buildColumn({ type: 'string', name: 'palletId', header: { renderer: () => html`<i>x</i>` } })\n\n expect(columnText(column)).to.equal('palletId')\n })\n\n it('invents no name when there is no column', () => {\n expect(columnText(undefined)).to.equal('')\n expect(columnText(null)).to.equal('')\n })\n})\n\ndescribe('row-select boxes are named', () => {\n const columns = [\n { type: 'gutter', gutterName: 'row-selector', name: '__selected__', multiple: true },\n { type: 'string', name: 'palletId', header: '파렛트 번호' }\n ].map(buildColumn)\n\n it('says which row each checkbox selects', async () => {\n const body: any = await fixture(html`\n <ox-grid-body\n .config=${{ ...ZERO_CONFIG, columns }}\n .columns=${columns}\n .data=${{ ...ZERO_DATA, records: [{ palletId: 'P-0001' }, { palletId: 'P-0002' }] }}\n ></ox-grid-body>\n `)\n\n await body.updateComplete\n\n /* The checkboxes live one shadow root deeper, inside each ox-grid-field. */\n const fields = Array.from(body.renderRoot.querySelectorAll('ox-grid-field')) as any[]\n\n await Promise.all(fields.map(field => field.updateComplete))\n\n const boxes = fields.flatMap(field =>\n Array.from(field.renderRoot?.querySelectorAll('input[type=checkbox], input[type=radio]') || [])\n ) as HTMLInputElement[]\n\n expect(boxes.length, 'found no row-select box').to.be.greaterThan(0)\n boxes.forEach(box =>\n expect(box.getAttribute('aria-label'), 'an unnamed checkbox is left').to.be.a('string').and.not.equal('')\n )\n })\n})\n\ndescribe('IME buffer names the cell it stands in for', () => {\n const columns = [\n { type: 'string', name: 'palletId', header: '파렛트 번호' },\n { type: 'string', name: 'location', header: '보관 위치' }\n ].map(buildColumn)\n\n async function bodyFocusedAt(column: number) {\n const body: any = await fixture(html`\n <ox-grid-body\n .config=${{ ...ZERO_CONFIG, columns }}\n .columns=${columns}\n .data=${{ ...ZERO_DATA, records: [{ palletId: 'P-0001', location: 'A-01' }] }}\n .focused=${{ row: 0, column }}\n ></ox-grid-body>\n `)\n\n await body.updateComplete\n\n return body.renderRoot.querySelector('#ime-buffer') as HTMLInputElement\n }\n\n it('reads the first column while focus sits there', async () => {\n expect((await bodyFocusedAt(0)).getAttribute('aria-label')).to.contain('파렛트 번호')\n })\n\n it('changes what it reads when focus moves a column over', async () => {\n expect((await bodyFocusedAt(1)).getAttribute('aria-label')).to.contain('보관 위치')\n })\n\n it('stays visible to assistive tech — it is where focus lands', async () => {\n expect((await bodyFocusedAt(0)).getAttribute('aria-hidden')).to.be.null\n })\n})\n\n/**\n * Table semantics.\n *\n * ## Why this is tested\n *\n * The grid is drawn with CSS grid: cells sit flat and there is no row element. Rows are a picture\n * made of equal offsetTop, so a screen reader saw a pile of inputs rather than a table — no \"this\n * is a table\", no \"row 3 column 3\", no \"40 rows in all\".\n *\n * Adding row wrappers can break two things at once:\n *\n * ```\n * semantics grid / row / columnheader / gridcell have to nest correctly\n * layout display: contents must generate no box; if it does, the columns collapse\n * ```\n *\n * The second one is why geometry is asserted: cells in a row must step right, and rows must step\n * down.\n */\ndescribe('grid declares itself a grid', () => {\n /* ox-grid takes already-compiled columns; compiling them is ox-grist's job. */\n const columns = [\n { type: 'gutter', gutterName: 'row-selector', name: '__selected__', multiple: true },\n { type: 'string', name: 'palletId', header: '파렛트 번호', sortable: true },\n { type: 'string', name: 'location', header: '보관 위치', sortable: true }\n ].map(buildColumn)\n\n async function grid(overrides: any = {}) {\n const el: any = await fixture(html`\n <ox-grid\n aria-label=\"입고 대기 목록\"\n .config=${{ ...ZERO_CONFIG, columns, rows: { ...ZERO_CONFIG.rows } }}\n .data=${{\n page: 1,\n limit: 20,\n total: 40,\n records: [\n { palletId: 'P-0001', location: 'A-01' },\n { palletId: 'P-0002', location: 'A-02' }\n ],\n ...overrides\n }}\n ></ox-grid>\n `)\n\n await el.updateComplete\n\n const header = el.renderRoot.querySelector('ox-grid-header')\n const body = el.renderRoot.querySelector('ox-grid-body')\n\n await header?.updateComplete\n await body?.updateComplete\n\n return { el, header, body }\n }\n\n it('nests grid, row and cell', async () => {\n const { el, header, body } = await grid()\n\n const grid_ = el.renderRoot.querySelector('[role=grid]')\n\n expect(grid_, 'no role=grid').to.exist\n expect(header.getAttribute('role')).to.equal('rowgroup')\n expect(body.getAttribute('role')).to.equal('rowgroup')\n\n expect(header.renderRoot.querySelectorAll('[role=row]').length).to.equal(1)\n expect(body.renderRoot.querySelectorAll('[role=row]').length).to.equal(2)\n expect(header.renderRoot.querySelectorAll('[role=columnheader]').length).to.equal(3)\n })\n\n it('counts the whole table, not the page on screen', async () => {\n const { el } = await grid()\n\n const grid_ = el.renderRoot.querySelector('[role=grid]')\n\n /* One header row plus 40 records. Two rows drawn is still a 40-row table. */\n expect(grid_.getAttribute('aria-rowcount')).to.equal('41')\n expect(grid_.getAttribute('aria-colcount')).to.equal('3')\n expect(grid_.getAttribute('aria-label')).to.equal('입고 대기 목록')\n })\n\n it('numbers page 2 from row 22, not back at 1', async () => {\n const { body } = await grid({ page: 2 })\n\n const rows = Array.from(body.renderRoot.querySelectorAll('[role=row]')) as HTMLElement[]\n\n /* One header row plus 20 records on page 1. */\n expect(rows[0].getAttribute('aria-rowindex')).to.equal('22')\n expect(rows[1].getAttribute('aria-rowindex')).to.equal('23')\n })\n\n it('numbers columns and leaves the width filler uncounted', async () => {\n const { body } = await grid()\n\n const row = body.renderRoot.querySelector('[role=row]') as HTMLElement\n const cells = Array.from(row.querySelectorAll('[role=gridcell]')) as HTMLElement[]\n\n expect(cells.map(cell => cell.getAttribute('aria-colindex'))).to.deep.equal(['1', '2', '3'])\n expect(row.querySelectorAll('[role=presentation]').length, 'the filler was counted as a cell').to.equal(1)\n })\n\n it('states sort direction in text, not only as an arrow', async () => {\n const el: any = await fixture(html`\n <ox-grid\n .config=${{ ...ZERO_CONFIG, columns, rows: { ...ZERO_CONFIG.rows } }}\n .sorters=${[{ name: 'palletId', desc: true }]}\n .data=${{ records: [{ palletId: 'P-0001', location: 'A-01' }] }}\n ></ox-grid>\n `)\n\n await el.updateComplete\n\n const header = el.renderRoot.querySelector('ox-grid-header')\n await header.updateComplete\n\n const headers = Array.from(header.renderRoot.querySelectorAll('[role=columnheader]')) as HTMLElement[]\n const sorts = headers.map(cell => cell.getAttribute('aria-sort'))\n\n /* A gutter cannot be sorted; claiming otherwise announces a control that is not there. */\n expect(sorts[0]).to.be.null\n expect(sorts[1], 'sorted descending').to.equal('descending')\n expect(sorts[2], 'sortable but not currently sorted').to.equal('none')\n })\n\n it('keeps the layout — a row wrapper that generates a box collapses the grid', async () => {\n const { body } = await grid()\n\n const rows = Array.from(body.renderRoot.querySelectorAll('[role=row]')) as HTMLElement[]\n const first = Array.from(rows[0].children) as HTMLElement[]\n\n expect(getComputedStyle(rows[0]).display).to.equal('contents')\n\n /* Cells step right; stacked at one x means the columns are gone. */\n const lefts = first.map(cell => cell.offsetLeft)\n expect(new Set(lefts).size, 'cells stacked at the same x').to.be.greaterThan(1)\n lefts.slice(1).forEach((left, index) => expect(left).to.be.greaterThan(lefts[index]))\n\n /* Rows step down. */\n const firstTop = (rows[0].children[0] as HTMLElement).offsetTop\n const secondTop = (rows[1].children[0] as HTMLElement).offsetTop\n expect(secondTop, 'two rows at the same y').to.be.greaterThan(firstTop)\n })\n})\n\n/**\n * Grouped headers.\n *\n * The header becomes two rows and ungrouped columns span both (`.span-both`). Placement runs\n * through CSS `grid-row` / `grid-column`, which makes this the riskiest spot for a row wrapper.\n */\ndescribe('grouped header spans two rows', () => {\n const columns = [\n { type: 'string', name: 'palletId', header: '파렛트 번호' },\n { type: 'string', name: 'width', header: { renderer: '폭', group: '치수' } },\n { type: 'string', name: 'height', header: { renderer: '높이', group: '치수' } }\n ].map(buildColumn)\n\n async function grouped() {\n const el: any = await fixture(html`\n <ox-grid\n .config=${{ ...ZERO_CONFIG, columns, rows: { ...ZERO_CONFIG.rows } }}\n .data=${{ total: 1, records: [{ palletId: 'P-0001', width: '1200', height: '900' }] }}\n ></ox-grid>\n `)\n\n await el.updateComplete\n\n const header = el.renderRoot.querySelector('ox-grid-header')\n const body = el.renderRoot.querySelector('ox-grid-body')\n\n await header.updateComplete\n await body.updateComplete\n\n return { el, header, body }\n }\n\n it('counts two header rows so the body starts at row 3', async () => {\n const { el, header, body } = await grouped()\n\n expect(header.renderRoot.querySelectorAll('[role=row]').length).to.equal(2)\n\n /* Two header rows plus one record. */\n expect(el.renderRoot.querySelector('[role=grid]').getAttribute('aria-rowcount')).to.equal('3')\n\n const rows = Array.from(body.renderRoot.querySelectorAll('[role=row]')) as HTMLElement[]\n expect(rows[0].getAttribute('aria-rowindex')).to.equal('3')\n })\n\n it('says how many columns a group covers', async () => {\n const { header } = await grouped()\n\n const cells = Array.from(header.renderRoot.querySelectorAll('[role=columnheader]')) as HTMLElement[]\n const first = cells[0]\n const group = cells.find(cell => cell.getAttribute('aria-colspan'))\n\n /* An ungrouped column spans both header rows. */\n expect(first.getAttribute('aria-rowspan')).to.equal('2')\n expect(first.getAttribute('aria-colindex')).to.equal('1')\n\n /* The group header covers columns 2 and 3. */\n expect(group, 'found no group header').to.exist\n expect(group!.getAttribute('aria-colindex')).to.equal('2')\n expect(group!.getAttribute('aria-colspan')).to.equal('2')\n })\n\n it('keeps two-row header placement — group sits above its leaf columns', async () => {\n const { header } = await grouped()\n\n const cells = Array.from(header.renderRoot.querySelectorAll('[role=columnheader]')) as HTMLElement[]\n const group = cells.find(cell => cell.getAttribute('aria-colspan'))!\n const leaf = cells.find(cell => !cell.getAttribute('aria-colspan') && cell !== cells[0])!\n\n expect(leaf.offsetTop, 'group header and its leaf column at the same height').to.be.greaterThan(group.offsetTop)\n expect(group.offsetWidth, 'group header does not cover two columns').to.be.greaterThan(leaf.offsetWidth)\n })\n})\n\n/**\n * The grid wrapper must not disturb the surrounding layout.\n *\n * `role=grid` admits only rows, so the settings-button slot and the pager moved outside the\n * wrapper. Two things then depend on this structure:\n *\n * ```\n * column layout the host is a flex column. A wrapper that generates a box swallows header\n * and body into one flex item, and the body's flex: 1 stops working\n * sibling selector \"show the settings button on header hover\" is an adjacent-sibling rule, so\n * the button has to sit directly after the wrapper\n * ```\n */\ndescribe('grid wrapper stays out of the layout', () => {\n const columns = [\n { type: 'string', name: 'palletId', header: '파렛트 번호' },\n { type: 'string', name: 'location', header: '보관 위치' }\n ].map(buildColumn)\n\n it('places the settings button after the grid, not inside it', async () => {\n const el: any = await fixture(html`\n <ox-grid\n .config=${{ ...ZERO_CONFIG, columns, rows: { ...ZERO_CONFIG.rows } }}\n .data=${{ total: 1, records: [{ palletId: 'P-0001', location: 'A-01' }] }}\n ></ox-grid>\n `)\n\n await el.updateComplete\n\n const grid = el.renderRoot.querySelector('[role=grid]') as HTMLElement\n const setting = el.renderRoot.querySelector('div[setting]') as HTMLElement\n\n expect(setting, 'no settings slot').to.exist\n expect(grid.contains(setting), 'settings button inside the grid breaks table reading').to.be.false\n expect(grid.nextElementSibling, 'adjacent-sibling rule no longer holds').to.equal(setting)\n })\n\n it('leaves header and body as flex items of the host', async () => {\n const el: any = await fixture(html`\n <div style=\"height: 300px; display: flex\">\n <ox-grid\n style=\"flex: 1\"\n .config=${{ ...ZERO_CONFIG, columns, rows: { ...ZERO_CONFIG.rows } }}\n .data=${{ total: 1, records: [{ palletId: 'P-0001', location: 'A-01' }] }}\n ></ox-grid>\n </div>\n `)\n\n const grid = el.querySelector('ox-grid') as any\n await grid.updateComplete\n\n const wrapper = grid.renderRoot.querySelector('[role=grid]') as HTMLElement\n const header = grid.renderRoot.querySelector('ox-grid-header') as HTMLElement\n const body = grid.renderRoot.querySelector('ox-grid-body') as HTMLElement\n\n expect(getComputedStyle(wrapper).display).to.equal('contents')\n\n /* The body takes the remaining height; a wrapper with a box shrinks it to header height. */\n expect(body.offsetHeight, 'body does not take the remaining height').to.be.greaterThan(100)\n expect(body.offsetTop, 'body is not below the header').to.be.greaterThan(0)\n expect(header.offsetTop).to.equal(0)\n })\n})\n"]}