@operato/data-grist 10.29.5 → 10.29.7
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/index.html +40 -40
- package/coverage/lcov-report/webpack/bootstrap.html +1 -1
- package/coverage/lcov-report/webpack/index.html +1 -1
- package/coverage/lcov-report/webpack/runtime/define property getters.html +1 -1
- package/coverage/lcov-report/webpack/runtime/global.html +1 -1
- package/coverage/lcov-report/webpack/runtime/hasOwnProperty shorthand.html +1 -1
- package/coverage/lcov-report/webpack/runtime/index.html +1 -1
- package/coverage/lcov-report/webpack/runtime/make namespace object.html +1 -1
- package/coverage/lcov.info +30482 -30377
- package/dist/src/data-card/data-card-field.js +3 -0
- package/dist/src/data-card/data-card-field.js.map +1 -1
- package/dist/src/data-grid/data-grid-accum-field.js +4 -0
- package/dist/src/data-grid/data-grid-accum-field.js.map +1 -1
- package/dist/src/data-grid/data-grid-body-style.js +2 -10
- package/dist/src/data-grid/data-grid-body-style.js.map +1 -1
- package/dist/src/data-grid/data-grid-body.d.ts +1 -0
- package/dist/src/data-grid/data-grid-body.js +20 -9
- package/dist/src/data-grid/data-grid-body.js.map +1 -1
- package/dist/src/data-grid/data-grid-field.d.ts +1 -0
- package/dist/src/data-grid/data-grid-field.js +29 -1
- package/dist/src/data-grid/data-grid-field.js.map +1 -1
- package/dist/src/data-grid/data-grid-header.js +5 -10
- package/dist/src/data-grid/data-grid-header.js.map +1 -1
- package/dist/src/data-grid/data-grid.d.ts +2 -1
- package/dist/src/data-grid/data-grid.js +72 -20
- package/dist/src/data-grid/data-grid.js.map +1 -1
- package/dist/test/accessible-name.test.js +6 -4
- package/dist/test/accessible-name.test.js.map +1 -1
- package/dist/test/field-contains-content.test.d.ts +1 -0
- package/dist/test/field-contains-content.test.js +101 -0
- package/dist/test/field-contains-content.test.js.map +1 -0
- package/dist/test/grid-single-scroller.test.d.ts +1 -0
- package/dist/test/grid-single-scroller.test.js +107 -0
- package/dist/test/grid-single-scroller.test.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/test/accessible-name.test.ts +6 -4
- package/test/field-contains-content.test.ts +125 -0
- package/test/grid-single-scroller.test.ts +150 -0
|
@@ -30,17 +30,19 @@ let DataGrid = class DataGrid extends DataManipulator {
|
|
|
30
30
|
this.fixedLefts = [];
|
|
31
31
|
}
|
|
32
32
|
firstUpdated() {
|
|
33
|
-
/*
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
if (
|
|
41
|
-
|
|
33
|
+
/*
|
|
34
|
+
* 머리와 몸은 같은 스크롤러 안에 있으므로 맞출 것이 없다(위 div[grid] 주석). 남는 것은 고정 열의
|
|
35
|
+
* 그림자 하나다 — 옆으로 밀렸을 때만 고정 열이 떠 보이게 한다. 값이 바뀔 때만 손댄다.
|
|
36
|
+
*/
|
|
37
|
+
let raised = false;
|
|
38
|
+
this.scroller.addEventListener('scroll', () => {
|
|
39
|
+
const next = this.scroller.scrollLeft > 0;
|
|
40
|
+
if (next !== raised) {
|
|
41
|
+
raised = next;
|
|
42
|
+
this.header.toggleAttribute('raised', raised);
|
|
43
|
+
this.body.toggleAttribute('raised', raised);
|
|
42
44
|
}
|
|
43
|
-
});
|
|
45
|
+
}, { passive: true });
|
|
44
46
|
this.header.addEventListener('wheel', this.onWheelEvent.bind(this));
|
|
45
47
|
this.footer && this.footer.addEventListener('wheel', this.onWheelEvent.bind(this));
|
|
46
48
|
/* record selection processing */
|
|
@@ -63,13 +65,23 @@ let DataGrid = class DataGrid extends DataManipulator {
|
|
|
63
65
|
}
|
|
64
66
|
});
|
|
65
67
|
}
|
|
68
|
+
/*
|
|
69
|
+
* 머리 · 꼬리 위에서 굴린 **세로** 휠을 가로 스크롤로 바꾼다.
|
|
70
|
+
*
|
|
71
|
+
* 가로 휠(트랙패드의 옆 밀기)은 건드리지 않는다 — 머리도 스크롤러 안에 있으므로 브라우저가 컴포지터에서
|
|
72
|
+
* 그대로 옮긴다. 손을 대면 그 부드러운 길을 메인 스레드로 끌어내리게 된다.
|
|
73
|
+
*/
|
|
66
74
|
onWheelEvent(e) {
|
|
67
|
-
if (
|
|
75
|
+
if (Math.abs(e.deltaX) >= Math.abs(e.deltaY)) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
const scroller = this.scroller;
|
|
79
|
+
if (scroller.scrollWidth > scroller.clientWidth) {
|
|
68
80
|
var delta = Math.max(-1, Math.min(1, e.deltaY || 0));
|
|
69
|
-
|
|
70
|
-
var maxScrollLeft =
|
|
71
|
-
var atStart =
|
|
72
|
-
var atEnd =
|
|
81
|
+
scroller.scrollLeft = Math.max(0, scroller.scrollLeft - delta * 30);
|
|
82
|
+
var maxScrollLeft = scroller.scrollWidth - scroller.clientWidth;
|
|
83
|
+
var atStart = scroller.scrollLeft === 0;
|
|
84
|
+
var atEnd = scroller.scrollLeft >= maxScrollLeft - 1;
|
|
73
85
|
// 스크롤이 맨 앞으로 와 있는 상태에서 휠을 올리는 경우 또는
|
|
74
86
|
// 스크롤이 맨 끝으로 가 있는 상태에서 휠을 내리는 경우에만 디폴트 동작 허용
|
|
75
87
|
if ((atStart && delta > 0) || (atEnd && delta < 0)) {
|
|
@@ -268,7 +280,7 @@ let DataGrid = class DataGrid extends DataManipulator {
|
|
|
268
280
|
this.body.focus();
|
|
269
281
|
}
|
|
270
282
|
get pullToRefreshTarget() {
|
|
271
|
-
return this.
|
|
283
|
+
return this.scroller;
|
|
272
284
|
}
|
|
273
285
|
};
|
|
274
286
|
DataGrid.styles = [
|
|
@@ -324,21 +336,58 @@ DataGrid.styles = [
|
|
|
324
336
|
}
|
|
325
337
|
|
|
326
338
|
/*
|
|
327
|
-
*
|
|
328
|
-
*
|
|
339
|
+
* ── 머리와 몸은 **한 스크롤러** 안에 있다 (2026-10-05) ──────────────────────
|
|
340
|
+
*
|
|
341
|
+
* 전에는 몸이 스크롤하고 머리는 따로 스크롤되는 상자였다. 몸의 scroll 이벤트를 받아 머리의
|
|
342
|
+
* scrollLeft 를 옮겼고, 머리 쪽에서도 거꾸로 몸을 옮겼다. 두 가지가 어긋났다.
|
|
343
|
+
*
|
|
344
|
+
* 이격 몸은 컴포지터가 곧바로 옮기고, 머리는 메인 스레드가 이벤트를 받은 다음에야 따라간다.
|
|
345
|
+
* 메인 스레드가 바쁜 화면일수록 머리가 몇 프레임씩 뒤처졌다
|
|
346
|
+
* 흔들림 양방향이라 늦게 도착한 머리의 이벤트가 몸을 **한 프레임 전 자리로 되돌렸다**
|
|
347
|
+
* 끝 두 상자의 스크롤 폭이 다르면(칸이 흘러 몸만 넓어지면) 오른쪽 끝에서 머리가 멈췄다
|
|
348
|
+
*
|
|
349
|
+
* 이제 role=grid 인 이 상자 하나가 두 방향을 다 스크롤한다. 머리는 위에 sticky 로 붙고, 몸은
|
|
350
|
+
* 스크롤하지 않고 내용만큼 넓어진다. 같은 상자 안에 있으므로 맞추는 코드가 없고, 어긋날 수도 없다.
|
|
329
351
|
*/
|
|
330
352
|
div[grid] {
|
|
331
|
-
|
|
353
|
+
flex: 1;
|
|
354
|
+
min-height: 0;
|
|
355
|
+
display: flex;
|
|
356
|
+
flex-direction: column;
|
|
357
|
+
|
|
358
|
+
overflow: auto;
|
|
359
|
+
overscroll-behavior: none;
|
|
360
|
+
/* 세로 스크롤바가 생기는 순간 열이 움찔하지 않게 자리를 늘 비워 둔다. */
|
|
361
|
+
scrollbar-gutter: stable;
|
|
332
362
|
}
|
|
333
363
|
|
|
364
|
+
/*
|
|
365
|
+
* 머리와 몸은 **열 폭의 합만큼** 넓다(width: max-content). 고정 열의 sticky 는 자기를 담은
|
|
366
|
+
* 상자 안에서만 움직이므로, 상자가 화면 폭에 머물면 그 너머로 스크롤했을 때 고정 열이 따라
|
|
367
|
+
* 밀려난다. 합이 화면보다 좁을 때는 화면을 채운다(min-width: 100%) — 마지막 auto 열이 남는
|
|
368
|
+
* 자리를 채운다.
|
|
369
|
+
*/
|
|
334
370
|
ox-grid-body {
|
|
335
|
-
flex: 1;
|
|
371
|
+
flex: 1 0 auto;
|
|
372
|
+
width: max-content;
|
|
373
|
+
min-width: 100%;
|
|
374
|
+
box-sizing: border-box;
|
|
336
375
|
position: relative;
|
|
376
|
+
/* 몸 안의 층(고정 열 2 · 선택 상자 5 · 입력 버퍼 10)을 몸 안에 가둔다. 머리는 그 위에 있다. */
|
|
377
|
+
z-index: 0;
|
|
337
378
|
|
|
338
379
|
font-variation-settings: 'FILL' 1;
|
|
339
380
|
}
|
|
340
381
|
|
|
341
382
|
ox-grid-header {
|
|
383
|
+
flex: none;
|
|
384
|
+
width: max-content;
|
|
385
|
+
min-width: 100%;
|
|
386
|
+
box-sizing: border-box;
|
|
387
|
+
position: sticky;
|
|
388
|
+
top: 0;
|
|
389
|
+
z-index: 1;
|
|
390
|
+
|
|
342
391
|
font-variation-settings: 'FILL' 1;
|
|
343
392
|
}
|
|
344
393
|
|
|
@@ -433,6 +482,9 @@ __decorate([
|
|
|
433
482
|
__decorate([
|
|
434
483
|
property({ type: Boolean, reflect: true, attribute: 'editable-grid' })
|
|
435
484
|
], DataGrid.prototype, "editableGrid", void 0);
|
|
485
|
+
__decorate([
|
|
486
|
+
query('div[grid]', true)
|
|
487
|
+
], DataGrid.prototype, "scroller", void 0);
|
|
436
488
|
__decorate([
|
|
437
489
|
query('ox-grid-body', true)
|
|
438
490
|
], DataGrid.prototype, "body", void 0);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data-grid.js","sourceRoot":"","sources":["../../../src/data-grid/data-grid.ts"],"names":[],"mappings":"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","sourcesContent":["import i18next from 'i18next'\nimport '@material/web/button/text-button.js'\nimport '@operato/list-state/ox-empty-note.js'\nimport './data-grid-header.js'\nimport './data-grid-body.js'\nimport './data-grid-footer.js'\n\nimport { css, html, LitElement, PropertyValues, nothing} from 'lit'\nimport { customElement, property, query, state } from 'lit/decorators.js'\nimport { ifDefined } from 'lit/directives/if-defined.js'\nimport { styles as MDTypeScaleStyles } from '@material/web/typography/md-typescale-styles.js'\n\nimport { ScrollbarStyles } from '@operato/styles'\n\nimport { DataManipulator } from '../data-manipulator.js'\nimport { ColumnConfig, GristRecord, PaginationConfig } from '../types.js'\nimport { GridEmptyNote, renderEmptyNote } from '../empty-note-render.js'\n// import { supportsPassive } from '../utils/index.js'\nimport { DataGridHeader } from './data-grid-header.js'\n\n/**\n * DataGrid\n */\n@customElement('ox-grid')\nexport class DataGrid extends DataManipulator {\n static styles = [\n MDTypeScaleStyles,\n ScrollbarStyles,\n css`\n /*\n * ── 고치는 표만 셀을 겨눈다 ─────────────────────────────────────────\n *\n * 셀 하나를 가리키는 점선과 열 폭 조절 손잡이는 **고칠 셀을 겨누는 장치**다. 기본 겉모습에서는\n * 둘을 두지 않는다(data-grist.ts 와 같은 값). 고칠 수 있는 열이 하나라도 있으면 여기서 되살린다.\n *\n * 화면이 고르는 모드가 아니다 — 표가 스스로 아는 사실이다(위 editableGrid 주석 참조).\n */\n :host([editable-grid]) {\n --grid-record-focused-cell-border: 1px dashed var(--ui-faint, #8a959c);\n /*\n * 정렬 표식의 자리는 이 값에서 **파생된다** — 손잡이가 보이면 표식이 칸 오른쪽 끝으로 가고,\n * 안 보이면 이름에 붙는다(data-grid-header.ts 의 스타일 질의). 여기서 따로 정하지 않는다.\n */\n --grid-header-splitter-display: flex;\n /*\n * ── 지나는 줄 표시를 **비우지 않는다** ──────────────────────────────\n *\n * 여기 hover 색을 transparent 로 두었었다. 「점선 셀 테두리가 이미 「지금 여기」를 말하므로\n * 줄 배경까지 움직일 필요가 없다」는 뜻이었는데, **그 값은 「칠하지 않는다」가 아니라\n * 「칸의 색을 지운다」였다.**\n *\n * 칸은 자기 바탕을 갖는다(흰색). 그것을 지우면 그 자리에 **구멍이 뚫려 표 뒤의 페이지가\n * 비친다.** 이 앱에서 그 색이 회색이라(rgb 240·242·244) 회색 띠처럼 보였고, 그래서\n * 「지나는 줄 표시」가 실제로는 있었다 — 아무도 정하지 않은 색으로.\n *\n * 그리고 **고정 열에서 그 띠가 끊긴다.** 고정 열은 스크롤되는 열 위에 떠 있어야 하니\n * 불투명해야 하고, 그래서 구멍이 안 뚫린다. 줄 하나가 두 색으로 갈렸다.\n *\n * 표 뒤에 무엇이 있느냐로 줄 색이 정해지는 것 자체가 결함이다. 어두운 페이지에 이 표를\n * 놓으면 마우스를 올린 줄이 검게 된다.\n *\n * 그래서 이 예외를 걷는다. 모든 표가 같은 hover 색을 쓰고, 그 색은 칸마다 실제로 칠하는\n * 색이므로 고정 열에도 똑같이 걸린다.\n */\n }\n\n :host {\n display: flex;\n flex-direction: column;\n\n /* div[setting] 의 절대 좌표 기준. */\n position: relative;\n overflow: hidden;\n\n border-width: var(--grid-container-border-width);\n }\n\n /*\n * Grid wrapper (role=grid) — generates no box, so header and body stay flex items of the\n * host.\n */\n div[grid] {\n display: contents;\n }\n\n ox-grid-body {\n flex: 1;\n position: relative;\n\n font-variation-settings: 'FILL' 1;\n }\n\n ox-grid-header {\n font-variation-settings: 'FILL' 1;\n }\n\n ox-grid-footer {\n font-variation-settings: 'FILL' 1;\n }\n\n ox-empty-note {\n display: block;\n position: absolute;\n left: 50%;\n top: 50%;\n transform: translate(-50%, -50%);\n }\n\n /*\n * 컬럼 설정 버튼은 **헤더에 마우스를 올릴 때만** 보인다.\n *\n * 늘 보이면 목록 화면마다 오른쪽 위에 눈에 걸리는 것이 하나 생긴다. 자주 쓰는 것도 아니다.\n *\n * 감춰 있는 동안에는 pointer-events 도 끈다 — 안 보이는데 눌리는 자리가 있으면, 헤더의 마지막\n * 컬럼을 누르려던 사람이 엉뚱한 팝업을 만난다.\n *\n * 세 갈래로 켠다.\n * 헤더에 올렸을 때 :has(ox-grid-header:hover)\n * 버튼 자체에 올렸을 때 버튼이 헤더 위에 떠 있으므로, 커서가 버튼으로 옮겨오면 헤더는 더 이상\n * hover 가 아니다. 이 갈래가 없으면 커서 아래에서 사라진다\n * 키보드 초점 focus-within\n */\n div[setting] {\n /*\n * 크기 없이 호스트의 오른쪽 위에 붙는다. 안에 들어오는 버튼이 [slot='setting'] 규칙으로\n * top: -3px · right: 0 을 스스로 잡으므로, 이 div 는 기준점 노릇만 한다.\n */\n position: absolute;\n top: 0;\n right: 0;\n width: 0;\n height: 0;\n z-index: 5;\n opacity: 0;\n pointer-events: none;\n transition: opacity 0.12s ease-out;\n }\n\n /*\n * 버튼 자체에 올렸을 때도 켜 둔다. 버튼이 헤더 위에 떠 있어서, 커서가 버튼으로 옮겨오면 헤더는\n * 더 이상 hover 가 아니다. 이 갈래가 없으면 커서 아래에서 사라진다.\n */\n div[grid]:has(ox-grid-header:hover) + div[setting],\n div[setting]:hover,\n div[setting]:focus-within {\n opacity: 1;\n pointer-events: auto;\n }\n\n /*\n * 손가락으로 쓰는 장치 — hover 가 없으므로 감추면 닿을 방법이 없다. 늘 두고 평소에는 흐리게,\n * 누르는 동안 선명하게 한다.\n *\n * 화면 폭이 아니라 입력 장치로 구분한다. 창을 좁힌 데스크톱은 여기에 걸리지 않고, 큰 태블릿은\n * 폭이 넓어도 걸린다.\n */\n @media (pointer: coarse) {\n div[setting] {\n opacity: 0.35;\n pointer-events: auto;\n }\n\n div[setting]:active,\n div[setting]:focus-within {\n opacity: 1;\n }\n }\n\n @media print {\n :host {\n zoom: 80%;\n }\n }\n `\n ]\n\n @property({ type: Object }) focused?: { row: number; column: number }\n @property({ type: Boolean }) empty?: boolean\n\n /** 비었을 때 무슨 말을 하나 — 판단은 data-grist 가 하고 여기서는 그리기만 한다. */\n @property({ type: Object }) emptyNote?: GridEmptyNote\n\n\n\n /**\n * 이 표에 고칠 수 있는 열이 하나라도 있는지. **겉모습이 이것으로 달라진다.**\n *\n * 셀 하나를 가리키는 점선과 열 폭 조절 손잡이는 **고칠 셀을 겨누는 장치**다. 고치지 않는 목록에서는\n * 셀 하나가 뜻을 갖지 않아, 줄을 고르려고 누른 자리에 점선이 남고 세로 선이 줄 읽기를 방해한다.\n *\n * 그래서 모드를 따로 두지 않는다. 화면이 고를 일이 아니라 **표가 스스로 아는 사실**이다 —\n * 열에 `record.editable` 이 있으면 고치는 표다.\n */\n @property({ type: Boolean, reflect: true, attribute: 'editable-grid' }) editableGrid: boolean = false\n\n @query('ox-grid-body', true) body!: LitElement\n @query('ox-grid-header', true) header!: DataGridHeader\n @query('ox-grid-footer', true) footer!: LitElement\n\n @state() fixedLefts: number[] = []\n\n firstUpdated() {\n /* header and body scroll synchronization */\n this.header.addEventListener('scroll', e => {\n if (this.body.scrollLeft !== this.header.scrollLeft) {\n this.body.scrollLeft = this.header.scrollLeft\n }\n })\n\n this.body.addEventListener('scroll', e => {\n if (this.body.scrollLeft !== this.header.scrollLeft) {\n this.header.scrollLeft = this.body.scrollLeft\n }\n })\n\n this.header.addEventListener('wheel', this.onWheelEvent.bind(this))\n this.footer && this.footer.addEventListener('wheel', this.onWheelEvent.bind(this))\n\n /* record selection processing */\n this.addEventListener('select-all-change', e => {\n if (this.data.records.length == 0) {\n return\n }\n\n var { selected } = (e as CustomEvent).detail\n var { records } = this.data\n\n records.forEach(record => (record['__selected__'] = selected))\n\n this.header.requestUpdate()\n this.body.requestUpdate()\n })\n\n this.addEventListener('focus-change', e => {\n const { row, column } = (e as CustomEvent).detail || {}\n if (!this.focused || this.focused.row != row || this.focused.column != column) {\n this.focused = { row, column }\n this.focus()\n this.requestUpdate()\n }\n })\n }\n\n onWheelEvent(e: WheelEvent) {\n if (this.body.scrollWidth > this.body.clientWidth) {\n var delta = Math.max(-1, Math.min(1, e.deltaY || 0))\n this.body.scrollLeft = Math.max(0, this.body.scrollLeft - delta * 30)\n\n var maxScrollLeft = this.body.scrollWidth - this.body.clientWidth\n\n var atStart = this.body.scrollLeft === 0\n var atEnd = this.body.scrollLeft === maxScrollLeft\n\n // 스크롤이 맨 앞으로 와 있는 상태에서 휠을 올리는 경우 또는\n // 스크롤이 맨 끝으로 가 있는 상태에서 휠을 내리는 경우에만 디폴트 동작 허용\n if ((atStart && delta > 0) || (atEnd && delta < 0)) {\n return true\n }\n\n e.preventDefault()\n }\n }\n\n onSelectRecordChanged({\n selectedRecords,\n added,\n removed\n }: {\n selectedRecords: GristRecord[]\n added: GristRecord[]\n removed: GristRecord[]\n }): void {\n super.onSelectRecordChanged({ selectedRecords, added, removed })\n\n this.header.requestUpdate()\n this.body.requestUpdate()\n }\n\n onRecordChanged(\n recordData: GristRecord,\n row: number,\n column: ColumnConfig | null /* TODO column should be removed */\n ) {\n /* record-viewer 등 grid 외에서 변경되는 경우에도 header와 body를 refresh 하도록 한다. */\n super.onRecordChanged(recordData, row, column)\n\n this.header.requestUpdate()\n this.body.requestUpdate()\n }\n\n updated(changes: PropertyValues<this>) {\n if (changes.has('data')) {\n /*\n * 데이타 내용에 따라 동적으로 컬럼의 폭이 달라지는 경우(예를 들면, sequence field)가 있으므로,\n * data의 변동에 대해서도 다시 계산되어야 한다.\n */\n this.calculateWidths(this.config.columns)\n\n if (this.focused && 'row' in this.focused) {\n var { row = 0, column = 0 } = this.focused\n var { records: oldrecords = [] } = changes.get('data') || {}\n var { records: newrecords } = this.data\n\n var oldrecord = oldrecords[row]\n\n if (oldrecord && oldrecord !== newrecords[row]) {\n var newrecord = newrecords.find(\n record =>\n /* TODO keyfields를 정의하고, keyfields의 동일성으로 확정해야 한다. */\n oldrecord === record ||\n ('id' in oldrecord\n ? record.id == oldrecord.id\n : 'name' in oldrecord\n ? record.name == oldrecord.name\n : false)\n )\n\n this.focused = {\n row: newrecord ? newrecords.indexOf(newrecord) : row,\n column\n }\n }\n }\n } else if (changes.has('config')) {\n this.calculateWidths(this.config.columns)\n }\n\n /* grid fetch 후에 grid로 focus를 주어야 하는 지 검토가 필요하다. */\n // this.focus()\n }\n\n calculateWidths(columns: ColumnConfig[]) {\n /*\n * 컬럼 모델 마지막에 'auto' cell을 추가하여, 자투리 영역을 꽉 채워서 표시한다.\n *\n * 숫자로 준 폭은 **그 폭 그대로** 쓴다. 합이 보이는 폭보다 크면 표는 가로로 넘치고, 나머지는\n * 가로 스크롤로 본다 — 표의 정상 거동이다.\n *\n * 한때 minmax(0, Npx) 로 두어 넘칠 때 열들이 함께 줄게 했다가 되돌렸다. 그렇게 하면 어떤 표도\n * 가로로 넘치지 않으므로 **가로 스크롤이 아예 사라진다.** 열 열다섯 개를 700px 에 밀어 넣으면\n * 모든 열이 못 읽을 만큼 좁아지고, 화면은 그것을 알릴 방법이 없다.\n *\n * 열이 좁아 잘리는 것은 화면이 열 폭을 정해서 풀 일이다 — 부품이 대신 줄이면 화면의 뜻이 사라진다.\n */\n const widths = columns\n .filter(column => !column.hidden)\n .map(column => {\n switch (typeof column.width) {\n case 'number':\n return `${column.width}px`\n case 'string':\n return column.width\n case 'function':\n return column.width.call(this, column)\n default:\n return 'auto'\n }\n })\n .concat(['auto'])\n .join(' ')\n\n /* 겉모습이 이 사실로 달라진다(위 editableGrid 주석 참조). 열이 정해지는 이 시점이 그 자리다. */\n this.editableGrid = columns.some(column => !column.hidden && !column.gutterName && !!column.record?.editable)\n\n this.style.setProperty('--grid-template-columns', widths)\n /* 인쇄는 종이 폭에 맞춰 비율로 나눈다. `220px` → `220fr` 로 바뀐다. */\n this.style.setProperty('--grid-template-print-columns', widths.replace(/px/gi, 'fr'))\n }\n\n /**\n * The grid's name, set by the caller as `<ox-grid aria-label=\"Inbound queue\">`.\n *\n * role=grid sits on the inner wrapper, and an aria-label on the host does not reach it, so the\n * value is taken and forwarded. No name is invented here: with several grids on one screen, which\n * one this is belongs to the screen, not to the component.\n */\n @property({ type: String, attribute: 'aria-label' }) gridLabel?: string\n\n render() {\n var { pagination = {} as PaginationConfig, columns = [], filters } = this.config\n\n var paginatable = !pagination.infinite\n var data = this.data\n\n /*\n * Declare the grid a grid.\n *\n * This component carried no roles at all — one aria- attribute in the entire source, on the\n * page number. To a screen reader the screen was a pile of inputs. Naming each cell by hand\n * still would not say \"this is a table\", \"row 3, column 3\", or \"40 rows in all\", and scanning\n * and comparing is the whole point of a table; without roles that navigation does not exist.\n *\n * With role=grid the column header becomes the cell's name without anything being written on\n * the cell. That is how an HTML table works, and why the pattern is specified that way.\n *\n * The extra wrapper is needed because a grid may only contain rows (or row groups). This\n * element also holds the settings-button slot and the pager, so putting the role on the host\n * mixes non-rows into the table and assistive tech drops out of table reading. Wrapping just\n * the header and body keeps them out. display: contents generates no box, so the host's column\n * flex layout is unchanged.\n */\n const headerRows = columns.some(column => !column.hidden && column.header?.group) ? 2 : 1\n const visibleColumns = columns.filter(column => !column.hidden).length\n const total = typeof data.total == 'number' && data.total > 0 ? data.total : data.records?.length || 0\n const page = Number(data.page) > 0 ? Number(data.page) : 1\n const limit = Number(data.limit) > 0 ? Number(data.limit) : data.records?.length || 0\n\n return html`\n <div\n grid\n role=\"grid\"\n aria-label=${ifDefined(this.gridLabel)}\n aria-rowcount=${headerRows + total}\n aria-colcount=${visibleColumns}\n >\n <ox-grid-header\n role=\"rowgroup\"\n class=\"md-typescale-label-medium-prominent\"\n .columns=${columns}\n .sorters=${this.sorters}\n .filters=${this.filters}\n .data=${data}\n ?filtering-feature=${filters?.header}\n @column-width-change=${(e: CustomEvent) => {\n let { idx, width } = e.detail\n columns[idx].width = width\n this.calculateWidths(columns)\n }}\n @fixed-lefts-change=${(e: CustomEvent) => (this.fixedLefts = e.detail)}\n >\n </ox-grid-header>\n\n <ox-grid-body\n role=\"rowgroup\"\n class=\"md-typescale-body-medium\"\n .config=${this.config}\n .columns=${columns}\n .data=${data}\n .focused=${this.focused!}\n .fixedLefts=${this.fixedLefts}\n .ariaRowIndexBase=${headerRows + (page - 1) * limit + 1}\n >\n ${this.empty ? renderEmptyNote(this.emptyNote) : html``}\n </ox-grid-body>\n </div>\n\n <!--\n Placed after the grid. \"Only while hovering the header\" needs a sibling selector, and a\n sibling selector can only reach forward.\n\n :host(:has(ox-grid-header:hover)) does not work — the argument of :host() is evaluated in\n the outer tree, so it never sees ox-grid-header inside the shadow tree. That was tried and\n did not react to hover. The :has() on the grid wrapper is evaluated inside the shadow tree,\n so it does.\n\n The settings button operates on the grid rather than being part of it, so it stays outside\n role=grid: a grid may only contain rows, and anything else makes assistive tech drop out of\n table reading.\n\n Reordering does not move it on screen — the style below pins it to the host's top right.\n -->\n <div setting>\n <slot name=\"setting\"></slot>\n </div>\n\n ${paginatable\n ? html`\n <!--\n 푸터에는 본문 타이프스케일을 씌우지 않는다.\n\n 그 클래스는 font 축약형으로 0.875rem 를 넣는데, **바깥 트리의 규칙이라 푸터의\n :host { font-size } 를 이긴다**(그림자 캐스케이드에서 호스트 요소는 바깥 트리 규칙이\n 우선한다). 그래서 푸터가 자기 크기를 못 정하고 본문과 같은 14px 로 남아 있었다.\n [확인함] 리터럴 :host { font-size: 7px } 도 안 먹었다. 2026-09-02\n\n 푸터는 본문이 아니다. 크기는 --grid-footer-font-size 가 정한다.\n -->\n <ox-grid-footer\n .data=${data}\n .pagination=${this.pagination}\n ></ox-grid-footer>\n `\n : html``}\n `\n }\n\n focus() {\n super.focus()\n\n this.body.focus()\n }\n\n get pullToRefreshTarget() {\n return this.body\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"data-grid.js","sourceRoot":"","sources":["../../../src/data-grid/data-grid.ts"],"names":[],"mappings":"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","sourcesContent":["import i18next from 'i18next'\nimport '@material/web/button/text-button.js'\nimport '@operato/list-state/ox-empty-note.js'\nimport './data-grid-header.js'\nimport './data-grid-body.js'\nimport './data-grid-footer.js'\n\nimport { css, html, LitElement, PropertyValues, nothing} from 'lit'\nimport { customElement, property, query, state } from 'lit/decorators.js'\nimport { ifDefined } from 'lit/directives/if-defined.js'\nimport { styles as MDTypeScaleStyles } from '@material/web/typography/md-typescale-styles.js'\n\nimport { ScrollbarStyles } from '@operato/styles'\n\nimport { DataManipulator } from '../data-manipulator.js'\nimport { ColumnConfig, GristRecord, PaginationConfig } from '../types.js'\nimport { GridEmptyNote, renderEmptyNote } from '../empty-note-render.js'\n// import { supportsPassive } from '../utils/index.js'\nimport { DataGridHeader } from './data-grid-header.js'\n\n/**\n * DataGrid\n */\n@customElement('ox-grid')\nexport class DataGrid extends DataManipulator {\n static styles = [\n MDTypeScaleStyles,\n ScrollbarStyles,\n css`\n /*\n * ── 고치는 표만 셀을 겨눈다 ─────────────────────────────────────────\n *\n * 셀 하나를 가리키는 점선과 열 폭 조절 손잡이는 **고칠 셀을 겨누는 장치**다. 기본 겉모습에서는\n * 둘을 두지 않는다(data-grist.ts 와 같은 값). 고칠 수 있는 열이 하나라도 있으면 여기서 되살린다.\n *\n * 화면이 고르는 모드가 아니다 — 표가 스스로 아는 사실이다(위 editableGrid 주석 참조).\n */\n :host([editable-grid]) {\n --grid-record-focused-cell-border: 1px dashed var(--ui-faint, #8a959c);\n /*\n * 정렬 표식의 자리는 이 값에서 **파생된다** — 손잡이가 보이면 표식이 칸 오른쪽 끝으로 가고,\n * 안 보이면 이름에 붙는다(data-grid-header.ts 의 스타일 질의). 여기서 따로 정하지 않는다.\n */\n --grid-header-splitter-display: flex;\n /*\n * ── 지나는 줄 표시를 **비우지 않는다** ──────────────────────────────\n *\n * 여기 hover 색을 transparent 로 두었었다. 「점선 셀 테두리가 이미 「지금 여기」를 말하므로\n * 줄 배경까지 움직일 필요가 없다」는 뜻이었는데, **그 값은 「칠하지 않는다」가 아니라\n * 「칸의 색을 지운다」였다.**\n *\n * 칸은 자기 바탕을 갖는다(흰색). 그것을 지우면 그 자리에 **구멍이 뚫려 표 뒤의 페이지가\n * 비친다.** 이 앱에서 그 색이 회색이라(rgb 240·242·244) 회색 띠처럼 보였고, 그래서\n * 「지나는 줄 표시」가 실제로는 있었다 — 아무도 정하지 않은 색으로.\n *\n * 그리고 **고정 열에서 그 띠가 끊긴다.** 고정 열은 스크롤되는 열 위에 떠 있어야 하니\n * 불투명해야 하고, 그래서 구멍이 안 뚫린다. 줄 하나가 두 색으로 갈렸다.\n *\n * 표 뒤에 무엇이 있느냐로 줄 색이 정해지는 것 자체가 결함이다. 어두운 페이지에 이 표를\n * 놓으면 마우스를 올린 줄이 검게 된다.\n *\n * 그래서 이 예외를 걷는다. 모든 표가 같은 hover 색을 쓰고, 그 색은 칸마다 실제로 칠하는\n * 색이므로 고정 열에도 똑같이 걸린다.\n */\n }\n\n :host {\n display: flex;\n flex-direction: column;\n\n /* div[setting] 의 절대 좌표 기준. */\n position: relative;\n overflow: hidden;\n\n border-width: var(--grid-container-border-width);\n }\n\n /*\n * ── 머리와 몸은 **한 스크롤러** 안에 있다 (2026-10-05) ──────────────────────\n *\n * 전에는 몸이 스크롤하고 머리는 따로 스크롤되는 상자였다. 몸의 scroll 이벤트를 받아 머리의\n * scrollLeft 를 옮겼고, 머리 쪽에서도 거꾸로 몸을 옮겼다. 두 가지가 어긋났다.\n *\n * 이격 몸은 컴포지터가 곧바로 옮기고, 머리는 메인 스레드가 이벤트를 받은 다음에야 따라간다.\n * 메인 스레드가 바쁜 화면일수록 머리가 몇 프레임씩 뒤처졌다\n * 흔들림 양방향이라 늦게 도착한 머리의 이벤트가 몸을 **한 프레임 전 자리로 되돌렸다**\n * 끝 두 상자의 스크롤 폭이 다르면(칸이 흘러 몸만 넓어지면) 오른쪽 끝에서 머리가 멈췄다\n *\n * 이제 role=grid 인 이 상자 하나가 두 방향을 다 스크롤한다. 머리는 위에 sticky 로 붙고, 몸은\n * 스크롤하지 않고 내용만큼 넓어진다. 같은 상자 안에 있으므로 맞추는 코드가 없고, 어긋날 수도 없다.\n */\n div[grid] {\n flex: 1;\n min-height: 0;\n display: flex;\n flex-direction: column;\n\n overflow: auto;\n overscroll-behavior: none;\n /* 세로 스크롤바가 생기는 순간 열이 움찔하지 않게 자리를 늘 비워 둔다. */\n scrollbar-gutter: stable;\n }\n\n /*\n * 머리와 몸은 **열 폭의 합만큼** 넓다(width: max-content). 고정 열의 sticky 는 자기를 담은\n * 상자 안에서만 움직이므로, 상자가 화면 폭에 머물면 그 너머로 스크롤했을 때 고정 열이 따라\n * 밀려난다. 합이 화면보다 좁을 때는 화면을 채운다(min-width: 100%) — 마지막 auto 열이 남는\n * 자리를 채운다.\n */\n ox-grid-body {\n flex: 1 0 auto;\n width: max-content;\n min-width: 100%;\n box-sizing: border-box;\n position: relative;\n /* 몸 안의 층(고정 열 2 · 선택 상자 5 · 입력 버퍼 10)을 몸 안에 가둔다. 머리는 그 위에 있다. */\n z-index: 0;\n\n font-variation-settings: 'FILL' 1;\n }\n\n ox-grid-header {\n flex: none;\n width: max-content;\n min-width: 100%;\n box-sizing: border-box;\n position: sticky;\n top: 0;\n z-index: 1;\n\n font-variation-settings: 'FILL' 1;\n }\n\n ox-grid-footer {\n font-variation-settings: 'FILL' 1;\n }\n\n ox-empty-note {\n display: block;\n position: absolute;\n left: 50%;\n top: 50%;\n transform: translate(-50%, -50%);\n }\n\n /*\n * 컬럼 설정 버튼은 **헤더에 마우스를 올릴 때만** 보인다.\n *\n * 늘 보이면 목록 화면마다 오른쪽 위에 눈에 걸리는 것이 하나 생긴다. 자주 쓰는 것도 아니다.\n *\n * 감춰 있는 동안에는 pointer-events 도 끈다 — 안 보이는데 눌리는 자리가 있으면, 헤더의 마지막\n * 컬럼을 누르려던 사람이 엉뚱한 팝업을 만난다.\n *\n * 세 갈래로 켠다.\n * 헤더에 올렸을 때 :has(ox-grid-header:hover)\n * 버튼 자체에 올렸을 때 버튼이 헤더 위에 떠 있으므로, 커서가 버튼으로 옮겨오면 헤더는 더 이상\n * hover 가 아니다. 이 갈래가 없으면 커서 아래에서 사라진다\n * 키보드 초점 focus-within\n */\n div[setting] {\n /*\n * 크기 없이 호스트의 오른쪽 위에 붙는다. 안에 들어오는 버튼이 [slot='setting'] 규칙으로\n * top: -3px · right: 0 을 스스로 잡으므로, 이 div 는 기준점 노릇만 한다.\n */\n position: absolute;\n top: 0;\n right: 0;\n width: 0;\n height: 0;\n z-index: 5;\n opacity: 0;\n pointer-events: none;\n transition: opacity 0.12s ease-out;\n }\n\n /*\n * 버튼 자체에 올렸을 때도 켜 둔다. 버튼이 헤더 위에 떠 있어서, 커서가 버튼으로 옮겨오면 헤더는\n * 더 이상 hover 가 아니다. 이 갈래가 없으면 커서 아래에서 사라진다.\n */\n div[grid]:has(ox-grid-header:hover) + div[setting],\n div[setting]:hover,\n div[setting]:focus-within {\n opacity: 1;\n pointer-events: auto;\n }\n\n /*\n * 손가락으로 쓰는 장치 — hover 가 없으므로 감추면 닿을 방법이 없다. 늘 두고 평소에는 흐리게,\n * 누르는 동안 선명하게 한다.\n *\n * 화면 폭이 아니라 입력 장치로 구분한다. 창을 좁힌 데스크톱은 여기에 걸리지 않고, 큰 태블릿은\n * 폭이 넓어도 걸린다.\n */\n @media (pointer: coarse) {\n div[setting] {\n opacity: 0.35;\n pointer-events: auto;\n }\n\n div[setting]:active,\n div[setting]:focus-within {\n opacity: 1;\n }\n }\n\n @media print {\n :host {\n zoom: 80%;\n }\n }\n `\n ]\n\n @property({ type: Object }) focused?: { row: number; column: number }\n @property({ type: Boolean }) empty?: boolean\n\n /** 비었을 때 무슨 말을 하나 — 판단은 data-grist 가 하고 여기서는 그리기만 한다. */\n @property({ type: Object }) emptyNote?: GridEmptyNote\n\n\n\n /**\n * 이 표에 고칠 수 있는 열이 하나라도 있는지. **겉모습이 이것으로 달라진다.**\n *\n * 셀 하나를 가리키는 점선과 열 폭 조절 손잡이는 **고칠 셀을 겨누는 장치**다. 고치지 않는 목록에서는\n * 셀 하나가 뜻을 갖지 않아, 줄을 고르려고 누른 자리에 점선이 남고 세로 선이 줄 읽기를 방해한다.\n *\n * 그래서 모드를 따로 두지 않는다. 화면이 고를 일이 아니라 **표가 스스로 아는 사실**이다 —\n * 열에 `record.editable` 이 있으면 고치는 표다.\n */\n @property({ type: Boolean, reflect: true, attribute: 'editable-grid' }) editableGrid: boolean = false\n\n @query('div[grid]', true) scroller!: HTMLElement\n @query('ox-grid-body', true) body!: LitElement\n @query('ox-grid-header', true) header!: DataGridHeader\n @query('ox-grid-footer', true) footer!: LitElement\n\n @state() fixedLefts: number[] = []\n\n firstUpdated() {\n /*\n * 머리와 몸은 같은 스크롤러 안에 있으므로 맞출 것이 없다(위 div[grid] 주석). 남는 것은 고정 열의\n * 그림자 하나다 — 옆으로 밀렸을 때만 고정 열이 떠 보이게 한다. 값이 바뀔 때만 손댄다.\n */\n let raised = false\n this.scroller.addEventListener(\n 'scroll',\n () => {\n const next = this.scroller.scrollLeft > 0\n if (next !== raised) {\n raised = next\n this.header.toggleAttribute('raised', raised)\n this.body.toggleAttribute('raised', raised)\n }\n },\n { passive: true }\n )\n\n this.header.addEventListener('wheel', this.onWheelEvent.bind(this))\n this.footer && this.footer.addEventListener('wheel', this.onWheelEvent.bind(this))\n\n /* record selection processing */\n this.addEventListener('select-all-change', e => {\n if (this.data.records.length == 0) {\n return\n }\n\n var { selected } = (e as CustomEvent).detail\n var { records } = this.data\n\n records.forEach(record => (record['__selected__'] = selected))\n\n this.header.requestUpdate()\n this.body.requestUpdate()\n })\n\n this.addEventListener('focus-change', e => {\n const { row, column } = (e as CustomEvent).detail || {}\n if (!this.focused || this.focused.row != row || this.focused.column != column) {\n this.focused = { row, column }\n this.focus()\n this.requestUpdate()\n }\n })\n }\n\n /*\n * 머리 · 꼬리 위에서 굴린 **세로** 휠을 가로 스크롤로 바꾼다.\n *\n * 가로 휠(트랙패드의 옆 밀기)은 건드리지 않는다 — 머리도 스크롤러 안에 있으므로 브라우저가 컴포지터에서\n * 그대로 옮긴다. 손을 대면 그 부드러운 길을 메인 스레드로 끌어내리게 된다.\n */\n onWheelEvent(e: WheelEvent) {\n if (Math.abs(e.deltaX) >= Math.abs(e.deltaY)) {\n return\n }\n\n const scroller = this.scroller\n if (scroller.scrollWidth > scroller.clientWidth) {\n var delta = Math.max(-1, Math.min(1, e.deltaY || 0))\n scroller.scrollLeft = Math.max(0, scroller.scrollLeft - delta * 30)\n\n var maxScrollLeft = scroller.scrollWidth - scroller.clientWidth\n\n var atStart = scroller.scrollLeft === 0\n var atEnd = scroller.scrollLeft >= maxScrollLeft - 1\n\n // 스크롤이 맨 앞으로 와 있는 상태에서 휠을 올리는 경우 또는\n // 스크롤이 맨 끝으로 가 있는 상태에서 휠을 내리는 경우에만 디폴트 동작 허용\n if ((atStart && delta > 0) || (atEnd && delta < 0)) {\n return true\n }\n\n e.preventDefault()\n }\n }\n\n onSelectRecordChanged({\n selectedRecords,\n added,\n removed\n }: {\n selectedRecords: GristRecord[]\n added: GristRecord[]\n removed: GristRecord[]\n }): void {\n super.onSelectRecordChanged({ selectedRecords, added, removed })\n\n this.header.requestUpdate()\n this.body.requestUpdate()\n }\n\n onRecordChanged(\n recordData: GristRecord,\n row: number,\n column: ColumnConfig | null /* TODO column should be removed */\n ) {\n /* record-viewer 등 grid 외에서 변경되는 경우에도 header와 body를 refresh 하도록 한다. */\n super.onRecordChanged(recordData, row, column)\n\n this.header.requestUpdate()\n this.body.requestUpdate()\n }\n\n updated(changes: PropertyValues<this>) {\n if (changes.has('data')) {\n /*\n * 데이타 내용에 따라 동적으로 컬럼의 폭이 달라지는 경우(예를 들면, sequence field)가 있으므로,\n * data의 변동에 대해서도 다시 계산되어야 한다.\n */\n this.calculateWidths(this.config.columns)\n\n if (this.focused && 'row' in this.focused) {\n var { row = 0, column = 0 } = this.focused\n var { records: oldrecords = [] } = changes.get('data') || {}\n var { records: newrecords } = this.data\n\n var oldrecord = oldrecords[row]\n\n if (oldrecord && oldrecord !== newrecords[row]) {\n var newrecord = newrecords.find(\n record =>\n /* TODO keyfields를 정의하고, keyfields의 동일성으로 확정해야 한다. */\n oldrecord === record ||\n ('id' in oldrecord\n ? record.id == oldrecord.id\n : 'name' in oldrecord\n ? record.name == oldrecord.name\n : false)\n )\n\n this.focused = {\n row: newrecord ? newrecords.indexOf(newrecord) : row,\n column\n }\n }\n }\n } else if (changes.has('config')) {\n this.calculateWidths(this.config.columns)\n }\n\n /* grid fetch 후에 grid로 focus를 주어야 하는 지 검토가 필요하다. */\n // this.focus()\n }\n\n calculateWidths(columns: ColumnConfig[]) {\n /*\n * 컬럼 모델 마지막에 'auto' cell을 추가하여, 자투리 영역을 꽉 채워서 표시한다.\n *\n * 숫자로 준 폭은 **그 폭 그대로** 쓴다. 합이 보이는 폭보다 크면 표는 가로로 넘치고, 나머지는\n * 가로 스크롤로 본다 — 표의 정상 거동이다.\n *\n * 한때 minmax(0, Npx) 로 두어 넘칠 때 열들이 함께 줄게 했다가 되돌렸다. 그렇게 하면 어떤 표도\n * 가로로 넘치지 않으므로 **가로 스크롤이 아예 사라진다.** 열 열다섯 개를 700px 에 밀어 넣으면\n * 모든 열이 못 읽을 만큼 좁아지고, 화면은 그것을 알릴 방법이 없다.\n *\n * 열이 좁아 잘리는 것은 화면이 열 폭을 정해서 풀 일이다 — 부품이 대신 줄이면 화면의 뜻이 사라진다.\n */\n const widths = columns\n .filter(column => !column.hidden)\n .map(column => {\n switch (typeof column.width) {\n case 'number':\n return `${column.width}px`\n case 'string':\n return column.width\n case 'function':\n return column.width.call(this, column)\n default:\n return 'auto'\n }\n })\n .concat(['auto'])\n .join(' ')\n\n /* 겉모습이 이 사실로 달라진다(위 editableGrid 주석 참조). 열이 정해지는 이 시점이 그 자리다. */\n this.editableGrid = columns.some(column => !column.hidden && !column.gutterName && !!column.record?.editable)\n\n this.style.setProperty('--grid-template-columns', widths)\n /* 인쇄는 종이 폭에 맞춰 비율로 나눈다. `220px` → `220fr` 로 바뀐다. */\n this.style.setProperty('--grid-template-print-columns', widths.replace(/px/gi, 'fr'))\n }\n\n /**\n * The grid's name, set by the caller as `<ox-grid aria-label=\"Inbound queue\">`.\n *\n * role=grid sits on the inner wrapper, and an aria-label on the host does not reach it, so the\n * value is taken and forwarded. No name is invented here: with several grids on one screen, which\n * one this is belongs to the screen, not to the component.\n */\n @property({ type: String, attribute: 'aria-label' }) gridLabel?: string\n\n render() {\n var { pagination = {} as PaginationConfig, columns = [], filters } = this.config\n\n var paginatable = !pagination.infinite\n var data = this.data\n\n /*\n * Declare the grid a grid.\n *\n * This component carried no roles at all — one aria- attribute in the entire source, on the\n * page number. To a screen reader the screen was a pile of inputs. Naming each cell by hand\n * still would not say \"this is a table\", \"row 3, column 3\", or \"40 rows in all\", and scanning\n * and comparing is the whole point of a table; without roles that navigation does not exist.\n *\n * With role=grid the column header becomes the cell's name without anything being written on\n * the cell. That is how an HTML table works, and why the pattern is specified that way.\n *\n * The extra wrapper is needed because a grid may only contain rows (or row groups). This\n * element also holds the settings-button slot and the pager, so putting the role on the host\n * mixes non-rows into the table and assistive tech drops out of table reading. Wrapping just\n * the header and body keeps them out. display: contents generates no box, so the host's column\n * flex layout is unchanged.\n */\n const headerRows = columns.some(column => !column.hidden && column.header?.group) ? 2 : 1\n const visibleColumns = columns.filter(column => !column.hidden).length\n const total = typeof data.total == 'number' && data.total > 0 ? data.total : data.records?.length || 0\n const page = Number(data.page) > 0 ? Number(data.page) : 1\n const limit = Number(data.limit) > 0 ? Number(data.limit) : data.records?.length || 0\n\n return html`\n <div\n grid\n role=\"grid\"\n aria-label=${ifDefined(this.gridLabel)}\n aria-rowcount=${headerRows + total}\n aria-colcount=${visibleColumns}\n >\n <ox-grid-header\n role=\"rowgroup\"\n class=\"md-typescale-label-medium-prominent\"\n .columns=${columns}\n .sorters=${this.sorters}\n .filters=${this.filters}\n .data=${data}\n ?filtering-feature=${filters?.header}\n @column-width-change=${(e: CustomEvent) => {\n let { idx, width } = e.detail\n columns[idx].width = width\n this.calculateWidths(columns)\n }}\n @fixed-lefts-change=${(e: CustomEvent) => (this.fixedLefts = e.detail)}\n >\n </ox-grid-header>\n\n <ox-grid-body\n role=\"rowgroup\"\n class=\"md-typescale-body-medium\"\n .config=${this.config}\n .columns=${columns}\n .data=${data}\n .focused=${this.focused!}\n .fixedLefts=${this.fixedLefts}\n .ariaRowIndexBase=${headerRows + (page - 1) * limit + 1}\n >\n ${this.empty ? renderEmptyNote(this.emptyNote) : html``}\n </ox-grid-body>\n </div>\n\n <!--\n Placed after the grid. \"Only while hovering the header\" needs a sibling selector, and a\n sibling selector can only reach forward.\n\n :host(:has(ox-grid-header:hover)) does not work — the argument of :host() is evaluated in\n the outer tree, so it never sees ox-grid-header inside the shadow tree. That was tried and\n did not react to hover. The :has() on the grid wrapper is evaluated inside the shadow tree,\n so it does.\n\n The settings button operates on the grid rather than being part of it, so it stays outside\n role=grid: a grid may only contain rows, and anything else makes assistive tech drop out of\n table reading.\n\n Reordering does not move it on screen — the style below pins it to the host's top right.\n -->\n <div setting>\n <slot name=\"setting\"></slot>\n </div>\n\n ${paginatable\n ? html`\n <!--\n 푸터에는 본문 타이프스케일을 씌우지 않는다.\n\n 그 클래스는 font 축약형으로 0.875rem 를 넣는데, **바깥 트리의 규칙이라 푸터의\n :host { font-size } 를 이긴다**(그림자 캐스케이드에서 호스트 요소는 바깥 트리 규칙이\n 우선한다). 그래서 푸터가 자기 크기를 못 정하고 본문과 같은 14px 로 남아 있었다.\n [확인함] 리터럴 :host { font-size: 7px } 도 안 먹었다. 2026-09-02\n\n 푸터는 본문이 아니다. 크기는 --grid-footer-font-size 가 정한다.\n -->\n <ox-grid-footer\n .data=${data}\n .pagination=${this.pagination}\n ></ox-grid-footer>\n `\n : html``}\n `\n }\n\n focus() {\n super.focus()\n\n this.body.focus()\n }\n\n get pullToRefreshTarget() {\n return this.scroller\n }\n}\n"]}
|
|
@@ -272,8 +272,9 @@ describe('grouped header spans two rows', () => {
|
|
|
272
272
|
* wrapper. Two things then depend on this structure:
|
|
273
273
|
*
|
|
274
274
|
* ```
|
|
275
|
-
* column layout the host is a flex column.
|
|
276
|
-
*
|
|
275
|
+
* column layout the host is a flex column. The wrapper is itself a flex column (it is the one
|
|
276
|
+
* scroller that header and body share), so the body still takes the height
|
|
277
|
+
* left under the header
|
|
277
278
|
* sibling selector "show the settings button on header hover" is an adjacent-sibling rule, so
|
|
278
279
|
* the button has to sit directly after the wrapper
|
|
279
280
|
* ```
|
|
@@ -297,7 +298,7 @@ describe('grid wrapper stays out of the layout', () => {
|
|
|
297
298
|
expect(grid.contains(setting), 'settings button inside the grid breaks table reading').to.be.false;
|
|
298
299
|
expect(grid.nextElementSibling, 'adjacent-sibling rule no longer holds').to.equal(setting);
|
|
299
300
|
});
|
|
300
|
-
it('
|
|
301
|
+
it('lays header and body out as a column that fills the host', async () => {
|
|
301
302
|
const el = await fixture(html `
|
|
302
303
|
<div style="height: 300px; display: flex">
|
|
303
304
|
<ox-grid
|
|
@@ -312,7 +313,8 @@ describe('grid wrapper stays out of the layout', () => {
|
|
|
312
313
|
const wrapper = grid.renderRoot.querySelector('[role=grid]');
|
|
313
314
|
const header = grid.renderRoot.querySelector('ox-grid-header');
|
|
314
315
|
const body = grid.renderRoot.querySelector('ox-grid-body');
|
|
315
|
-
expect(getComputedStyle(wrapper).display).to.equal('
|
|
316
|
+
expect(getComputedStyle(wrapper).display).to.equal('flex');
|
|
317
|
+
expect(getComputedStyle(wrapper).flexDirection).to.equal('column');
|
|
316
318
|
/* The body takes the remaining height; a wrapper with a box shrinks it to header height. */
|
|
317
319
|
expect(body.offsetHeight, 'body does not take the remaining height').to.be.greaterThan(100);
|
|
318
320
|
expect(body.offsetTop, 'body is not below the header').to.be.greaterThan(0);
|
|
@@ -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 * 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"]}
|
|
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 * 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. The wrapper is itself a flex column (it is the one\n * scroller that header and body share), so the body still takes the height\n * left under the header\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('lays header and body out as a column that fills 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('flex')\n expect(getComputedStyle(wrapper).flexDirection).to.equal('column')\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"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import '../src/data-grist.js';
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/* Elements are compared by presence, not by value: a failing element comparison makes chai render the DOM and hang the runner. */
|
|
2
|
+
import { expect, fixture } from '@open-wc/testing';
|
|
3
|
+
import { html } from 'lit';
|
|
4
|
+
import '../src/data-grist.js';
|
|
5
|
+
/**
|
|
6
|
+
* A cell keeps what its renderer draws inside its column.
|
|
7
|
+
*
|
|
8
|
+
* `FieldRenderer` may return a plain string, and the cell rendered it as a bare text node. Only
|
|
9
|
+
* `span` and `pre` were clipped, so a long string — or an `<a>` — spilled past its track. The body's
|
|
10
|
+
* scrollWidth grew past the header's (the header is just the sum of the column widths): at the far
|
|
11
|
+
* right the header stopped while the body kept scrolling, and stray text showed where there were no
|
|
12
|
+
* columns. Header and body now share one scroller, so the overflow shows as the grid scrolling past
|
|
13
|
+
* its last column instead.
|
|
14
|
+
*
|
|
15
|
+
* [measured] operato-office contract list — body 5503 / header 3830 (2026-10-04)
|
|
16
|
+
*
|
|
17
|
+
* The long cells sit in the last column, because only content that runs past the last track widens
|
|
18
|
+
* the body; in a middle column the next cell paints over it and the defect hides.
|
|
19
|
+
*/
|
|
20
|
+
const LONG = '긴 글자가 열 너비를 넘는다 '.repeat(40);
|
|
21
|
+
const settle = (ms = 150) => new Promise(resolve => setTimeout(resolve, ms));
|
|
22
|
+
async function grid(renderer) {
|
|
23
|
+
const config = {
|
|
24
|
+
columns: [
|
|
25
|
+
{ type: 'string', name: 'id', hidden: true },
|
|
26
|
+
{ type: 'string', name: 'name', header: 'name', width: 120 },
|
|
27
|
+
{ type: 'string', name: 'note', header: 'note', width: 160, record: { renderer } }
|
|
28
|
+
],
|
|
29
|
+
pagination: { pages: [20] }
|
|
30
|
+
};
|
|
31
|
+
const el = (await fixture(html `
|
|
32
|
+
<ox-grist
|
|
33
|
+
style="width: 400px; height: 300px"
|
|
34
|
+
mode="GRID"
|
|
35
|
+
.config=${config}
|
|
36
|
+
.fetchHandler=${async () => ({
|
|
37
|
+
total: 2,
|
|
38
|
+
records: [
|
|
39
|
+
{ id: '1', name: 'a', note: LONG },
|
|
40
|
+
{ id: '2', name: 'b', note: LONG }
|
|
41
|
+
]
|
|
42
|
+
})}
|
|
43
|
+
></ox-grist>
|
|
44
|
+
`));
|
|
45
|
+
await el.fetch();
|
|
46
|
+
await settle();
|
|
47
|
+
await el.updateComplete;
|
|
48
|
+
const grist = el.renderRoot.querySelector('#grist');
|
|
49
|
+
const scroller = grist.renderRoot.querySelector('div[grid]');
|
|
50
|
+
const header = grist.renderRoot.querySelector('ox-grid-header');
|
|
51
|
+
const body = grist.renderRoot.querySelector('ox-grid-body');
|
|
52
|
+
await body.updateComplete;
|
|
53
|
+
const notes = Array.from(body.renderRoot.querySelectorAll('ox-grid-field')).filter(f => { var _a; return ((_a = f.column) === null || _a === void 0 ? void 0 : _a.name) === 'note'; });
|
|
54
|
+
return { scroller, header, body, notes };
|
|
55
|
+
}
|
|
56
|
+
describe('a grid cell keeps its content inside its column', () => {
|
|
57
|
+
/* Resizing the grid body inside a fixture trips Chrome's benign "ResizeObserver loop" error. */
|
|
58
|
+
const ignoreResizeLoop = (e) => {
|
|
59
|
+
if (String(e.message).includes('ResizeObserver loop')) {
|
|
60
|
+
e.stopImmediatePropagation();
|
|
61
|
+
e.preventDefault();
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
let previousOnError = null;
|
|
65
|
+
before(() => {
|
|
66
|
+
window.addEventListener('error', ignoreResizeLoop, true);
|
|
67
|
+
previousOnError = window.onerror;
|
|
68
|
+
window.onerror = (message, ...rest) => String(message).includes('ResizeObserver loop') ? true : previousOnError === null || previousOnError === void 0 ? void 0 : previousOnError.call(window, message, ...rest);
|
|
69
|
+
});
|
|
70
|
+
after(() => {
|
|
71
|
+
window.removeEventListener('error', ignoreResizeLoop, true);
|
|
72
|
+
window.onerror = previousOnError;
|
|
73
|
+
});
|
|
74
|
+
const renderers = [
|
|
75
|
+
['a plain string', value => String(value)],
|
|
76
|
+
['an <a>', value => html `<a href="#">${value}</a>`]
|
|
77
|
+
];
|
|
78
|
+
for (const [kind, renderer] of renderers) {
|
|
79
|
+
it(`${kind}: the grid scrolls exactly as far as its columns reach`, async () => {
|
|
80
|
+
const { scroller, header, body, notes } = await grid(renderer);
|
|
81
|
+
expect(notes.length, 'the long cells must be drawn, or this measures nothing').to.be.at.least(2);
|
|
82
|
+
expect(body.offsetWidth, 'header and body span the same width').to.equal(header.offsetWidth);
|
|
83
|
+
expect(scroller.scrollWidth, 'content past the last column scrolls on into empty space').to.equal(header.offsetWidth);
|
|
84
|
+
});
|
|
85
|
+
it(`${kind}: the cell is as wide as its column and clips what does not fit`, async () => {
|
|
86
|
+
const { notes } = await grid(renderer);
|
|
87
|
+
for (const note of notes) {
|
|
88
|
+
expect(Math.round(note.getBoundingClientRect().width)).to.equal(160);
|
|
89
|
+
expect(getComputedStyle(note).overflowX).to.equal('hidden');
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
it('a plain string still ends in an ellipsis', async () => {
|
|
94
|
+
const { notes } = await grid(value => String(value));
|
|
95
|
+
const text = notes[0].renderRoot.querySelector('span');
|
|
96
|
+
expect(!!text, 'the string sits in a box that can take text-overflow').to.equal(true);
|
|
97
|
+
expect(getComputedStyle(text).textOverflow).to.equal('ellipsis');
|
|
98
|
+
expect(text.clientWidth).to.be.lessThan(160);
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
//# sourceMappingURL=field-contains-content.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"field-contains-content.test.js","sourceRoot":"","sources":["../../test/field-contains-content.test.ts"],"names":[],"mappings":"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","sourcesContent":["/* Elements are compared by presence, not by value: a failing element comparison makes chai render the DOM and hang the runner. */\nimport { expect, fixture } from '@open-wc/testing'\nimport { html } from 'lit'\n\nimport '../src/data-grist.js'\n\n/**\n * A cell keeps what its renderer draws inside its column.\n *\n * `FieldRenderer` may return a plain string, and the cell rendered it as a bare text node. Only\n * `span` and `pre` were clipped, so a long string — or an `<a>` — spilled past its track. The body's\n * scrollWidth grew past the header's (the header is just the sum of the column widths): at the far\n * right the header stopped while the body kept scrolling, and stray text showed where there were no\n * columns. Header and body now share one scroller, so the overflow shows as the grid scrolling past\n * its last column instead.\n *\n * [measured] operato-office contract list — body 5503 / header 3830 (2026-10-04)\n *\n * The long cells sit in the last column, because only content that runs past the last track widens\n * the body; in a middle column the next cell paints over it and the defect hides.\n */\n\nconst LONG = '긴 글자가 열 너비를 넘는다 '.repeat(40)\n\nconst settle = (ms = 150) => new Promise(resolve => setTimeout(resolve, ms))\n\ntype Root = HTMLElement & { renderRoot: ShadowRoot; updateComplete: Promise<unknown> }\n\nasync function grid(renderer: (value: unknown) => unknown) {\n const config = {\n columns: [\n { type: 'string', name: 'id', hidden: true },\n { type: 'string', name: 'name', header: 'name', width: 120 },\n { type: 'string', name: 'note', header: 'note', width: 160, record: { renderer } }\n ],\n pagination: { pages: [20] }\n }\n\n const el = (await fixture(html`\n <ox-grist\n style=\"width: 400px; height: 300px\"\n mode=\"GRID\"\n .config=${config}\n .fetchHandler=${async () => ({\n total: 2,\n records: [\n { id: '1', name: 'a', note: LONG },\n { id: '2', name: 'b', note: LONG }\n ]\n })}\n ></ox-grist>\n `)) as Root & { fetch(): Promise<void> }\n\n await el.fetch()\n await settle()\n await el.updateComplete\n\n const grist = el.renderRoot.querySelector('#grist') as Root\n const scroller = grist.renderRoot.querySelector('div[grid]') as HTMLElement\n const header = grist.renderRoot.querySelector('ox-grid-header') as Root\n const body = grist.renderRoot.querySelector('ox-grid-body') as Root\n await body.updateComplete\n\n const notes = Array.from(body.renderRoot.querySelectorAll('ox-grid-field')).filter(\n f => (f as any).column?.name === 'note'\n ) as Root[]\n\n return { scroller, header, body, notes }\n}\n\ndescribe('a grid cell keeps its content inside its column', () => {\n /* Resizing the grid body inside a fixture trips Chrome's benign \"ResizeObserver loop\" error. */\n const ignoreResizeLoop = (e: ErrorEvent) => {\n if (String(e.message).includes('ResizeObserver loop')) {\n e.stopImmediatePropagation()\n e.preventDefault()\n }\n }\n let previousOnError: OnErrorEventHandler = null\n before(() => {\n window.addEventListener('error', ignoreResizeLoop, true)\n previousOnError = window.onerror\n window.onerror = (message, ...rest) =>\n String(message).includes('ResizeObserver loop') ? true : previousOnError?.call(window, message, ...rest)\n })\n after(() => {\n window.removeEventListener('error', ignoreResizeLoop, true)\n window.onerror = previousOnError\n })\n\n const renderers: [string, (value: unknown) => unknown][] = [\n ['a plain string', value => String(value)],\n ['an <a>', value => html`<a href=\"#\">${value}</a>`]\n ]\n\n for (const [kind, renderer] of renderers) {\n it(`${kind}: the grid scrolls exactly as far as its columns reach`, async () => {\n const { scroller, header, body, notes } = await grid(renderer)\n\n expect(notes.length, 'the long cells must be drawn, or this measures nothing').to.be.at.least(2)\n expect(body.offsetWidth, 'header and body span the same width').to.equal(header.offsetWidth)\n expect(scroller.scrollWidth, 'content past the last column scrolls on into empty space').to.equal(\n header.offsetWidth\n )\n })\n\n it(`${kind}: the cell is as wide as its column and clips what does not fit`, async () => {\n const { notes } = await grid(renderer)\n\n for (const note of notes) {\n expect(Math.round(note.getBoundingClientRect().width)).to.equal(160)\n expect(getComputedStyle(note).overflowX).to.equal('hidden')\n }\n })\n }\n\n it('a plain string still ends in an ellipsis', async () => {\n const { notes } = await grid(value => String(value))\n\n const text = notes[0].renderRoot.querySelector('span') as HTMLElement | null\n expect(!!text, 'the string sits in a box that can take text-overflow').to.equal(true)\n expect(getComputedStyle(text!).textOverflow).to.equal('ellipsis')\n expect(text!.clientWidth).to.be.lessThan(160)\n })\n})\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import '../src/data-grist.js';
|