@operato/data-grist 10.9.0 → 10.10.0
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 +15 -0
- package/dist/src/configure/column-builder.js +17 -2
- package/dist/src/configure/column-builder.js.map +1 -1
- package/dist/src/configure/config-builder.js +1 -1
- package/dist/src/configure/config-builder.js.map +1 -1
- package/dist/src/data-grid/data-grid-body-style.js +12 -1
- package/dist/src/data-grid/data-grid-body-style.js.map +1 -1
- package/dist/src/data-grid/data-grid-body.d.ts +24 -0
- package/dist/src/data-grid/data-grid-body.js +126 -39
- package/dist/src/data-grid/data-grid-body.js.map +1 -1
- package/dist/src/data-grid/data-grid-field.d.ts +2 -2
- package/dist/src/data-grid/data-grid-field.js +29 -29
- package/dist/src/data-grid/data-grid-field.js.map +1 -1
- package/dist/src/data-grid/data-grid-footer.js +1 -1
- package/dist/src/data-grid/data-grid-footer.js.map +1 -1
- package/dist/src/data-grid/data-grid-header.d.ts +12 -1
- package/dist/src/data-grid/data-grid-header.js +81 -8
- package/dist/src/data-grid/data-grid-header.js.map +1 -1
- package/dist/src/data-grid/data-grid.d.ts +9 -1
- package/dist/src/data-grid/data-grid.js +79 -27
- package/dist/src/data-grid/data-grid.js.map +1 -1
- package/dist/src/data-grist.d.ts +7 -0
- package/dist/src/data-grist.js +12 -8
- package/dist/src/data-grist.js.map +1 -1
- package/dist/src/editors/ox-grist-editor.d.ts +14 -1
- package/dist/src/editors/ox-grist-editor.js +27 -3
- package/dist/src/editors/ox-grist-editor.js.map +1 -1
- package/dist/src/editors/registry.d.ts +2 -2
- package/dist/src/editors/registry.js +2 -2
- package/dist/src/editors/registry.js.map +1 -1
- package/dist/src/filters/filter-input.js +15 -0
- package/dist/src/filters/filter-input.js.map +1 -1
- package/dist/src/filters/filter-range-date.js +9 -0
- package/dist/src/filters/filter-range-date.js.map +1 -1
- package/dist/src/filters/filter-range-number.js +9 -0
- package/dist/src/filters/filter-range-number.js.map +1 -1
- package/dist/src/gutters/gutter-row-selector.js +16 -0
- package/dist/src/gutters/gutter-row-selector.js.map +1 -1
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +1 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/renderers/ox-grist-renderer-boolean.js +8 -0
- package/dist/src/renderers/ox-grist-renderer-boolean.js.map +1 -1
- package/dist/src/types.d.ts +11 -1
- package/dist/src/types.js.map +1 -1
- package/dist/src/utils/column-text.d.ts +18 -0
- package/dist/src/utils/column-text.js +37 -0
- package/dist/src/utils/column-text.js.map +1 -0
- package/dist/src/utils/index.d.ts +1 -0
- package/dist/src/utils/index.js +1 -0
- package/dist/src/utils/index.js.map +1 -1
- package/dist/test/accessible-name.test.d.ts +2 -0
- package/dist/test/accessible-name.test.js +324 -0
- package/dist/test/accessible-name.test.js.map +1 -0
- package/dist/test/row-hover-visible.test.js +2 -2
- package/dist/test/row-hover-visible.test.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +7 -6
- package/test/accessible-name.test.ts +408 -0
- package/test/row-hover-visible.test.ts +2 -2
- package/translations/en.json +8 -0
- package/translations/ja.json +8 -0
- package/translations/ko.json +7 -0
- package/translations/ms.json +8 -0
- package/translations/zh.json +8 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* column 의 이름을 **글자로** 돌려준다.
|
|
3
|
+
*
|
|
4
|
+
* header renderer 는 화면에 그릴 것을 만드는 함수다 — 돌려주는 값이 lit template 이라 거기서 이름을
|
|
5
|
+
* 되꺼낼 수 없다. 그런데 화면 밖에서 이름이 필요한 자리가 있다.
|
|
6
|
+
*
|
|
7
|
+
* 스크린 리더 칸에 들어간 입력칸이 무엇을 입력하는 칸인지 말해야 한다
|
|
8
|
+
* filter 입력칸 머리줄의 입력칸은 그 column 을 좁히는 것인데, 이름 없이 상자만 있었다
|
|
9
|
+
* IME buffer 지금 어느 칸을 두드리고 있는지
|
|
10
|
+
*
|
|
11
|
+
* 이 함수가 이름의 **유일한 출처**다. 부르는 쪽이 각자 이름을 다시 적지 않는다 — 그러면 header 를
|
|
12
|
+
* 고쳤을 때 나머지가 옛 이름으로 남는다.
|
|
13
|
+
*
|
|
14
|
+
* 찾는 순서는 사람이 화면에서 읽는 순서와 같다: 머리줄 이름 → label → column 이름.
|
|
15
|
+
* 마지막의 column.name 은 개발자가 붙인 식별자라 뜻이 없을 수 있지만, 이름이 아예 없는 것보다는 낫다.
|
|
16
|
+
*/
|
|
17
|
+
export function columnText(column) {
|
|
18
|
+
if (!column) {
|
|
19
|
+
return '';
|
|
20
|
+
}
|
|
21
|
+
const header = column.header;
|
|
22
|
+
if (typeof header == 'string') {
|
|
23
|
+
return header;
|
|
24
|
+
}
|
|
25
|
+
if (typeof (header === null || header === void 0 ? void 0 : header.text) == 'string') {
|
|
26
|
+
return header.text;
|
|
27
|
+
}
|
|
28
|
+
const label = column.label;
|
|
29
|
+
if (typeof label == 'string') {
|
|
30
|
+
return label;
|
|
31
|
+
}
|
|
32
|
+
if (typeof (label === null || label === void 0 ? void 0 : label.text) == 'string') {
|
|
33
|
+
return label.text;
|
|
34
|
+
}
|
|
35
|
+
return column.name || '';
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=column-text.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"column-text.js","sourceRoot":"","sources":["../../../src/utils/column-text.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,UAAU,CAAC,MAAqC;IAC9D,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,EAAE,CAAA;IACX,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,CAAC,MAAa,CAAA;IAEnC,IAAI,OAAO,MAAM,IAAI,QAAQ,EAAE,CAAC;QAC9B,OAAO,MAAM,CAAA;IACf,CAAC;IAED,IAAI,OAAO,CAAA,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,IAAI,CAAA,IAAI,QAAQ,EAAE,CAAC;QACpC,OAAO,MAAM,CAAC,IAAI,CAAA;IACpB,CAAC;IAED,MAAM,KAAK,GAAG,MAAM,CAAC,KAAY,CAAA;IAEjC,IAAI,OAAO,KAAK,IAAI,QAAQ,EAAE,CAAC;QAC7B,OAAO,KAAK,CAAA;IACd,CAAC;IAED,IAAI,OAAO,CAAA,KAAK,aAAL,KAAK,uBAAL,KAAK,CAAE,IAAI,CAAA,IAAI,QAAQ,EAAE,CAAC;QACnC,OAAO,KAAK,CAAC,IAAI,CAAA;IACnB,CAAC;IAED,OAAO,MAAM,CAAC,IAAI,IAAI,EAAE,CAAA;AAC1B,CAAC","sourcesContent":["import { ColumnConfig } from '../types.js'\n\n/**\n * column 의 이름을 **글자로** 돌려준다.\n *\n * header renderer 는 화면에 그릴 것을 만드는 함수다 — 돌려주는 값이 lit template 이라 거기서 이름을\n * 되꺼낼 수 없다. 그런데 화면 밖에서 이름이 필요한 자리가 있다.\n *\n * 스크린 리더 칸에 들어간 입력칸이 무엇을 입력하는 칸인지 말해야 한다\n * filter 입력칸 머리줄의 입력칸은 그 column 을 좁히는 것인데, 이름 없이 상자만 있었다\n * IME buffer 지금 어느 칸을 두드리고 있는지\n *\n * 이 함수가 이름의 **유일한 출처**다. 부르는 쪽이 각자 이름을 다시 적지 않는다 — 그러면 header 를\n * 고쳤을 때 나머지가 옛 이름으로 남는다.\n *\n * 찾는 순서는 사람이 화면에서 읽는 순서와 같다: 머리줄 이름 → label → column 이름.\n * 마지막의 column.name 은 개발자가 붙인 식별자라 뜻이 없을 수 있지만, 이름이 아예 없는 것보다는 낫다.\n */\nexport function columnText(column?: Partial<ColumnConfig> | null): string {\n if (!column) {\n return ''\n }\n\n const header = column.header as any\n\n if (typeof header == 'string') {\n return header\n }\n\n if (typeof header?.text == 'string') {\n return header.text\n }\n\n const label = column.label as any\n\n if (typeof label == 'string') {\n return label\n }\n\n if (typeof label?.text == 'string') {\n return label.text\n }\n\n return column.name || ''\n}\n"]}
|
package/dist/src/utils/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAA;AACrC,cAAc,iBAAiB,CAAA","sourcesContent":["export * from './supports-passive.js'\nexport * from './list-param.js'\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAA;AACrC,cAAc,kBAAkB,CAAA;AAChC,cAAc,iBAAiB,CAAA","sourcesContent":["export * from './supports-passive.js'\nexport * from './column-text.js'\nexport * from './list-param.js'\n"]}
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
import { expect, fixture } from '@open-wc/testing';
|
|
2
|
+
import { html } from 'lit';
|
|
3
|
+
import '../src/data-grid/data-grid.js';
|
|
4
|
+
import '../src/data-grid/data-grid-body.js';
|
|
5
|
+
import { buildColumn } from '../src/configure/column-builder.js';
|
|
6
|
+
import { columnText } from '../src/utils/column-text.js';
|
|
7
|
+
import { ZERO_CONFIG, ZERO_DATA } from '../src/configure/zero-config.js';
|
|
8
|
+
/**
|
|
9
|
+
* 입력칸의 이름.
|
|
10
|
+
*
|
|
11
|
+
* ## 왜 시험이 필요한가
|
|
12
|
+
*
|
|
13
|
+
* 이 grid 안에는 이름 없는 입력칸이 여럿 있었다 — 줄을 고르는 체크박스, 머리줄의 filter 상자,
|
|
14
|
+
* 그리고 포커스를 담고 있는 IME buffer. 눈으로 보는 사람에게는 **자리**가 뜻을 말해 준다(맨 왼쪽
|
|
15
|
+
* 좁은 칸이면 줄 선택, 머리줄 이름 바로 아래면 그 column 의 filter). 그런데 자리는 그림일 뿐이고
|
|
16
|
+
* 관계가 아니라서, 화면을 못 보는 사람에게는 이름 없는 상자들만 남는다.
|
|
17
|
+
*
|
|
18
|
+
* ## 무엇을 지키려는가
|
|
19
|
+
*
|
|
20
|
+
* 이름의 출처가 하나여야 한다. `header` 를 고쳤을 때 읽히는 이름도 함께 바뀌어야 하고, 그러려면
|
|
21
|
+
* 부르는 쪽이 이름을 다시 적어 두면 안 된다. `columnText()` 가 그 유일한 출처다.
|
|
22
|
+
*
|
|
23
|
+
* `buildColumn` 이 문자열 header 를 `() => header` 함수로 바꿔 버리는 자리가 있었는데, 그 함수가
|
|
24
|
+
* 돌려주는 것은 lit template 이라 이름을 되꺼낼 수 없다 — 여기서 이름이 사라지고 있었다.
|
|
25
|
+
*/
|
|
26
|
+
describe('column 이름의 출처는 하나다', () => {
|
|
27
|
+
it('문자열로 준 header 는 함수로 바뀌어도 글자가 남는다', () => {
|
|
28
|
+
const column = buildColumn({ type: 'string', name: 'palletId', header: '파렛트 번호' });
|
|
29
|
+
expect(columnText(column)).to.equal('파렛트 번호');
|
|
30
|
+
});
|
|
31
|
+
it('header.renderer 에 문자열을 준 경우도 같다', () => {
|
|
32
|
+
const column = buildColumn({ type: 'string', name: 'palletId', header: { renderer: '파렛트 번호' } });
|
|
33
|
+
expect(columnText(column)).to.equal('파렛트 번호');
|
|
34
|
+
});
|
|
35
|
+
it('label: true 는 header 의 이름을 물려받는다', () => {
|
|
36
|
+
const column = buildColumn({ type: 'string', name: 'palletId', header: '파렛트 번호', label: true });
|
|
37
|
+
expect(columnText(column)).to.equal('파렛트 번호');
|
|
38
|
+
});
|
|
39
|
+
it('header 를 함수로 그리면 이름을 되꺼낼 수 없다 — 그때는 column 이름으로 물러난다', () => {
|
|
40
|
+
const column = buildColumn({ type: 'string', name: 'palletId', header: { renderer: () => html `<i>x</i>` } });
|
|
41
|
+
expect(columnText(column)).to.equal('palletId');
|
|
42
|
+
});
|
|
43
|
+
it('column 이 없으면 이름을 지어내지 않는다', () => {
|
|
44
|
+
expect(columnText(undefined)).to.equal('');
|
|
45
|
+
expect(columnText(null)).to.equal('');
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
describe('줄을 고르는 칸에는 이름이 있다', () => {
|
|
49
|
+
const columns = [
|
|
50
|
+
{ type: 'gutter', gutterName: 'row-selector', name: '__selected__', multiple: true },
|
|
51
|
+
{ type: 'string', name: 'palletId', header: '파렛트 번호' }
|
|
52
|
+
].map(buildColumn);
|
|
53
|
+
it('행마다 놓인 체크박스가 몇 번째 줄인지 말한다', async () => {
|
|
54
|
+
const body = await fixture(html `
|
|
55
|
+
<ox-grid-body
|
|
56
|
+
.config=${{ ...ZERO_CONFIG, columns }}
|
|
57
|
+
.columns=${columns}
|
|
58
|
+
.data=${{ ...ZERO_DATA, records: [{ palletId: 'P-0001' }, { palletId: 'P-0002' }] }}
|
|
59
|
+
></ox-grid-body>
|
|
60
|
+
`);
|
|
61
|
+
await body.updateComplete;
|
|
62
|
+
/* 체크박스는 각 칸(ox-grid-field)의 shadow root 안에 그려진다 — 한 겹 더 들어가야 보인다. */
|
|
63
|
+
const fields = Array.from(body.renderRoot.querySelectorAll('ox-grid-field'));
|
|
64
|
+
await Promise.all(fields.map(field => field.updateComplete));
|
|
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(''));
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
describe('포커스를 담고 있는 IME buffer 가 지금 선 칸을 말한다', () => {
|
|
71
|
+
const columns = [
|
|
72
|
+
{ type: 'string', name: 'palletId', header: '파렛트 번호' },
|
|
73
|
+
{ type: 'string', name: 'location', header: '보관 위치' }
|
|
74
|
+
].map(buildColumn);
|
|
75
|
+
async function bodyFocusedAt(column) {
|
|
76
|
+
const body = await fixture(html `
|
|
77
|
+
<ox-grid-body
|
|
78
|
+
.config=${{ ...ZERO_CONFIG, columns }}
|
|
79
|
+
.columns=${columns}
|
|
80
|
+
.data=${{ ...ZERO_DATA, records: [{ palletId: 'P-0001', location: 'A-01' }] }}
|
|
81
|
+
.focused=${{ row: 0, column }}
|
|
82
|
+
></ox-grid-body>
|
|
83
|
+
`);
|
|
84
|
+
await body.updateComplete;
|
|
85
|
+
return body.renderRoot.querySelector('#ime-buffer');
|
|
86
|
+
}
|
|
87
|
+
it('첫 칸에 서 있으면 첫 칸의 이름을 읽는다', async () => {
|
|
88
|
+
expect((await bodyFocusedAt(0)).getAttribute('aria-label')).to.contain('파렛트 번호');
|
|
89
|
+
});
|
|
90
|
+
it('옆 칸으로 옮기면 읽히는 이름도 바뀐다 — 안 바뀌면 어디에 선 것인지 알 수 없다', async () => {
|
|
91
|
+
expect((await bodyFocusedAt(1)).getAttribute('aria-label')).to.contain('보관 위치');
|
|
92
|
+
});
|
|
93
|
+
it('보조기술에서 지우지 않는다 — 포커스를 받는 자리다', async () => {
|
|
94
|
+
expect((await bodyFocusedAt(0)).getAttribute('aria-hidden')).to.be.null;
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
/**
|
|
98
|
+
* 표를 표라고 선언했나.
|
|
99
|
+
*
|
|
100
|
+
* ## 왜 시험이 필요한가
|
|
101
|
+
*
|
|
102
|
+
* 이 표는 CSS grid 로 그린다 — 칸이 평평하게 놓여 있고 **줄이라는 요소가 없다.** 보기에는 줄이
|
|
103
|
+
* 있지만 그것은 같은 높이에 놓였다는 그림일 뿐이다. 그래서 스크린 리더에게 이 화면은 「여기가
|
|
104
|
+
* 표다」도, 「지금 3행 3열이다」도, 「전체는 40행이다」도 아닌 **입력칸 더미**였다.
|
|
105
|
+
*
|
|
106
|
+
* 줄 감싸개를 넣으면서 두 가지가 동시에 깨질 수 있다.
|
|
107
|
+
*
|
|
108
|
+
* ```
|
|
109
|
+
* 뜻이 서나 role=grid / row / columnheader / gridcell 이 제대로 겹쳐 있나
|
|
110
|
+
* 모양이 사나 display: contents 가 정말로 상자를 안 만드나 — 만들면 표가 한 줄로 무너진다
|
|
111
|
+
* ```
|
|
112
|
+
*
|
|
113
|
+
* 두 번째가 특히 중요하다. `display: contents` 를 잘못 걸면 칸들이 grid 항목이 아니게 되어
|
|
114
|
+
* 열이 사라진다. 그래서 **자리를 실제로 확인한다** — 한 줄 안의 칸은 왼쪽으로 갈수록 x 가 커지고,
|
|
115
|
+
* 줄과 줄은 y 가 달라야 한다.
|
|
116
|
+
*/
|
|
117
|
+
describe('표를 표라고 선언한다', () => {
|
|
118
|
+
/* ox-grid 는 이미 다듬어진 column 을 받는다 — 다듬는 것은 ox-grist 의 일이다. */
|
|
119
|
+
const columns = [
|
|
120
|
+
{ type: 'gutter', gutterName: 'row-selector', name: '__selected__', multiple: true },
|
|
121
|
+
{ type: 'string', name: 'palletId', header: '파렛트 번호', sortable: true },
|
|
122
|
+
{ type: 'string', name: 'location', header: '보관 위치', sortable: true }
|
|
123
|
+
].map(buildColumn);
|
|
124
|
+
async function grid(overrides = {}) {
|
|
125
|
+
const el = await fixture(html `
|
|
126
|
+
<ox-grid
|
|
127
|
+
aria-label="입고 대기 목록"
|
|
128
|
+
.config=${{ ...ZERO_CONFIG, columns, rows: { ...ZERO_CONFIG.rows } }}
|
|
129
|
+
.data=${{
|
|
130
|
+
page: 1,
|
|
131
|
+
limit: 20,
|
|
132
|
+
total: 40,
|
|
133
|
+
records: [
|
|
134
|
+
{ palletId: 'P-0001', location: 'A-01' },
|
|
135
|
+
{ palletId: 'P-0002', location: 'A-02' }
|
|
136
|
+
],
|
|
137
|
+
...overrides
|
|
138
|
+
}}
|
|
139
|
+
></ox-grid>
|
|
140
|
+
`);
|
|
141
|
+
await el.updateComplete;
|
|
142
|
+
const header = el.renderRoot.querySelector('ox-grid-header');
|
|
143
|
+
const body = el.renderRoot.querySelector('ox-grid-body');
|
|
144
|
+
await (header === null || header === void 0 ? void 0 : header.updateComplete);
|
|
145
|
+
await (body === null || body === void 0 ? void 0 : body.updateComplete);
|
|
146
|
+
return { el, header, body };
|
|
147
|
+
}
|
|
148
|
+
it('표 · 줄 · 칸이 겹쳐 있다', async () => {
|
|
149
|
+
const { el, header, body } = await grid();
|
|
150
|
+
const grid_ = el.renderRoot.querySelector('[role=grid]');
|
|
151
|
+
expect(grid_, 'role=grid 가 없다').to.exist;
|
|
152
|
+
expect(header.getAttribute('role')).to.equal('rowgroup');
|
|
153
|
+
expect(body.getAttribute('role')).to.equal('rowgroup');
|
|
154
|
+
expect(header.renderRoot.querySelectorAll('[role=row]').length).to.equal(1);
|
|
155
|
+
expect(body.renderRoot.querySelectorAll('[role=row]').length).to.equal(2);
|
|
156
|
+
expect(header.renderRoot.querySelectorAll('[role=columnheader]').length).to.equal(3);
|
|
157
|
+
});
|
|
158
|
+
it('전체가 몇 줄 몇 열인지 말한다 — 지금 페이지가 아니라', async () => {
|
|
159
|
+
const { el } = await grid();
|
|
160
|
+
const grid_ = el.renderRoot.querySelector('[role=grid]');
|
|
161
|
+
/* 머리 한 줄 + 전체 40건. 화면에 두 줄만 그려도 40건짜리 표다. */
|
|
162
|
+
expect(grid_.getAttribute('aria-rowcount')).to.equal('41');
|
|
163
|
+
expect(grid_.getAttribute('aria-colcount')).to.equal('3');
|
|
164
|
+
expect(grid_.getAttribute('aria-label')).to.equal('입고 대기 목록');
|
|
165
|
+
});
|
|
166
|
+
it('2페이지의 첫 줄은 21번째 줄이다 — 페이지마다 1번으로 돌아가지 않는다', async () => {
|
|
167
|
+
const { body } = await grid({ page: 2 });
|
|
168
|
+
const rows = Array.from(body.renderRoot.querySelectorAll('[role=row]'));
|
|
169
|
+
/* 머리 한 줄(1) + 앞 페이지 20건 = 22번째부터 */
|
|
170
|
+
expect(rows[0].getAttribute('aria-rowindex')).to.equal('22');
|
|
171
|
+
expect(rows[1].getAttribute('aria-rowindex')).to.equal('23');
|
|
172
|
+
});
|
|
173
|
+
it('칸이 몇 번째 열인지 말한다 — 남는 폭을 메우는 칸은 세지 않는다', async () => {
|
|
174
|
+
const { body } = await grid();
|
|
175
|
+
const row = body.renderRoot.querySelector('[role=row]');
|
|
176
|
+
const cells = Array.from(row.querySelectorAll('[role=gridcell]'));
|
|
177
|
+
expect(cells.map(cell => cell.getAttribute('aria-colindex'))).to.deep.equal(['1', '2', '3']);
|
|
178
|
+
expect(row.querySelectorAll('[role=presentation]').length, '메우는 칸이 표의 칸으로 세어졌다').to.equal(1);
|
|
179
|
+
});
|
|
180
|
+
it('정렬 상태를 글자로도 말한다 — 화살표는 눈으로만 읽힌다', async () => {
|
|
181
|
+
const el = await fixture(html `
|
|
182
|
+
<ox-grid
|
|
183
|
+
.config=${{ ...ZERO_CONFIG, columns, rows: { ...ZERO_CONFIG.rows } }}
|
|
184
|
+
.sorters=${[{ name: 'palletId', desc: true }]}
|
|
185
|
+
.data=${{ records: [{ palletId: 'P-0001', location: 'A-01' }] }}
|
|
186
|
+
></ox-grid>
|
|
187
|
+
`);
|
|
188
|
+
await el.updateComplete;
|
|
189
|
+
const header = el.renderRoot.querySelector('ox-grid-header');
|
|
190
|
+
await header.updateComplete;
|
|
191
|
+
const headers = Array.from(header.renderRoot.querySelectorAll('[role=columnheader]'));
|
|
192
|
+
const sorts = headers.map(cell => cell.getAttribute('aria-sort'));
|
|
193
|
+
/* 거터는 정렬할 수 없다 — 있지도 않은 기능을 있다고 말하지 않는다. */
|
|
194
|
+
expect(sorts[0]).to.be.null;
|
|
195
|
+
expect(sorts[1], '내림차순으로 정렬된 열').to.equal('descending');
|
|
196
|
+
expect(sorts[2], '정렬할 수 있으나 지금은 아닌 열').to.equal('none');
|
|
197
|
+
});
|
|
198
|
+
it('줄 감싸개가 배치를 건드리지 않는다 — 상자를 만들면 표가 무너진다', async () => {
|
|
199
|
+
const { body } = await grid();
|
|
200
|
+
const rows = Array.from(body.renderRoot.querySelectorAll('[role=row]'));
|
|
201
|
+
const first = Array.from(rows[0].children);
|
|
202
|
+
/* 감싸개 자신은 상자가 없다 */
|
|
203
|
+
expect(getComputedStyle(rows[0]).display).to.equal('contents');
|
|
204
|
+
/* 한 줄 안의 칸은 오른쪽으로 나아간다 — 한 자리에 겹쳐 있으면 열이 사라진 것이다 */
|
|
205
|
+
const lefts = first.map(cell => cell.offsetLeft);
|
|
206
|
+
expect(new Set(lefts).size, '칸들이 같은 x 에 겹쳐 있다').to.be.greaterThan(1);
|
|
207
|
+
lefts.slice(1).forEach((left, index) => expect(left).to.be.greaterThan(lefts[index]));
|
|
208
|
+
/* 줄과 줄은 아래로 내려간다 */
|
|
209
|
+
const firstTop = rows[0].children[0].offsetTop;
|
|
210
|
+
const secondTop = rows[1].children[0].offsetTop;
|
|
211
|
+
expect(secondTop, '두 줄이 같은 y 에 있다').to.be.greaterThan(firstTop);
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
/**
|
|
215
|
+
* 묶음 머리가 있는 표.
|
|
216
|
+
*
|
|
217
|
+
* 머리가 두 줄이 되고, 묶이지 않은 열은 두 줄에 걸친다(`.span-both`). 배치가 CSS grid 의
|
|
218
|
+
* `grid-row` / `grid-column` 으로 이루어지므로 **줄 감싸개가 가장 위험한 자리**다.
|
|
219
|
+
*/
|
|
220
|
+
describe('묶음 머리 — 머리가 두 줄이다', () => {
|
|
221
|
+
const columns = [
|
|
222
|
+
{ type: 'string', name: 'palletId', header: '파렛트 번호' },
|
|
223
|
+
{ type: 'string', name: 'width', header: { renderer: '폭', group: '치수' } },
|
|
224
|
+
{ type: 'string', name: 'height', header: { renderer: '높이', group: '치수' } }
|
|
225
|
+
].map(buildColumn);
|
|
226
|
+
async function grouped() {
|
|
227
|
+
const el = await fixture(html `
|
|
228
|
+
<ox-grid
|
|
229
|
+
.config=${{ ...ZERO_CONFIG, columns, rows: { ...ZERO_CONFIG.rows } }}
|
|
230
|
+
.data=${{ total: 1, records: [{ palletId: 'P-0001', width: '1200', height: '900' }] }}
|
|
231
|
+
></ox-grid>
|
|
232
|
+
`);
|
|
233
|
+
await el.updateComplete;
|
|
234
|
+
const header = el.renderRoot.querySelector('ox-grid-header');
|
|
235
|
+
const body = el.renderRoot.querySelector('ox-grid-body');
|
|
236
|
+
await header.updateComplete;
|
|
237
|
+
await body.updateComplete;
|
|
238
|
+
return { el, header, body };
|
|
239
|
+
}
|
|
240
|
+
it('머리가 두 줄로 세어진다 — 본문은 3번째 줄부터', async () => {
|
|
241
|
+
const { el, header, body } = await grouped();
|
|
242
|
+
expect(header.renderRoot.querySelectorAll('[role=row]').length).to.equal(2);
|
|
243
|
+
/* 머리 두 줄 + 1건 */
|
|
244
|
+
expect(el.renderRoot.querySelector('[role=grid]').getAttribute('aria-rowcount')).to.equal('3');
|
|
245
|
+
const rows = Array.from(body.renderRoot.querySelectorAll('[role=row]'));
|
|
246
|
+
expect(rows[0].getAttribute('aria-rowindex')).to.equal('3');
|
|
247
|
+
});
|
|
248
|
+
it('묶음 머리가 몇 열을 덮는지 말한다', async () => {
|
|
249
|
+
const { header } = await grouped();
|
|
250
|
+
const cells = Array.from(header.renderRoot.querySelectorAll('[role=columnheader]'));
|
|
251
|
+
const first = cells[0];
|
|
252
|
+
const group = cells.find(cell => cell.getAttribute('aria-colspan'));
|
|
253
|
+
/* 묶이지 않은 열은 두 줄에 걸친다 */
|
|
254
|
+
expect(first.getAttribute('aria-rowspan')).to.equal('2');
|
|
255
|
+
expect(first.getAttribute('aria-colindex')).to.equal('1');
|
|
256
|
+
/* 치수 묶음은 2 · 3열을 덮는다 */
|
|
257
|
+
expect(group, '묶음 머리를 찾지 못했다').to.exist;
|
|
258
|
+
expect(group.getAttribute('aria-colindex')).to.equal('2');
|
|
259
|
+
expect(group.getAttribute('aria-colspan')).to.equal('2');
|
|
260
|
+
});
|
|
261
|
+
it('두 줄짜리 머리의 배치가 그대로다 — 묶음이 아래 줄보다 위에 있다', async () => {
|
|
262
|
+
const { header } = await grouped();
|
|
263
|
+
const cells = Array.from(header.renderRoot.querySelectorAll('[role=columnheader]'));
|
|
264
|
+
const group = cells.find(cell => cell.getAttribute('aria-colspan'));
|
|
265
|
+
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);
|
|
268
|
+
});
|
|
269
|
+
});
|
|
270
|
+
/**
|
|
271
|
+
* 표 감싸개가 바깥 배치를 건드리지 않나.
|
|
272
|
+
*
|
|
273
|
+
* `role=grid` 안에는 줄만 들어갈 수 있어서, 설정 단추 자리와 페이지 넘김은 감싸개 **밖**으로
|
|
274
|
+
* 나왔다. 그러면서 두 가지가 이 구조에 매인다.
|
|
275
|
+
*
|
|
276
|
+
* ```
|
|
277
|
+
* 세로 배치 호스트는 flex column 이다. 감싸개가 상자를 만들면 머리와 몸이 그 안으로 들어가
|
|
278
|
+
* flex 항목이 하나가 되고, 몸의 flex: 1 이 듣지 않아 높이가 무너진다
|
|
279
|
+
* 형제 선택자 「머리에 올렸을 때 설정 단추를 보인다」가 인접 형제 선택자다. 설정 단추가 감싸개
|
|
280
|
+
* 바로 뒤에 있어야 그 규칙이 성립한다
|
|
281
|
+
* ```
|
|
282
|
+
*/
|
|
283
|
+
describe('표 감싸개는 배치에 끼어들지 않는다', () => {
|
|
284
|
+
const columns = [
|
|
285
|
+
{ type: 'string', name: 'palletId', header: '파렛트 번호' },
|
|
286
|
+
{ type: 'string', name: 'location', header: '보관 위치' }
|
|
287
|
+
].map(buildColumn);
|
|
288
|
+
it('설정 단추는 표 바로 뒤에 있다 — 표 안이 아니라', async () => {
|
|
289
|
+
const el = await fixture(html `
|
|
290
|
+
<ox-grid
|
|
291
|
+
.config=${{ ...ZERO_CONFIG, columns, rows: { ...ZERO_CONFIG.rows } }}
|
|
292
|
+
.data=${{ total: 1, records: [{ palletId: 'P-0001', location: 'A-01' }] }}
|
|
293
|
+
></ox-grid>
|
|
294
|
+
`);
|
|
295
|
+
await el.updateComplete;
|
|
296
|
+
const grid = el.renderRoot.querySelector('[role=grid]');
|
|
297
|
+
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);
|
|
301
|
+
});
|
|
302
|
+
it('머리와 몸이 그대로 호스트의 세로 배치를 쓴다', async () => {
|
|
303
|
+
const el = await fixture(html `
|
|
304
|
+
<div style="height: 300px; display: flex">
|
|
305
|
+
<ox-grid
|
|
306
|
+
style="flex: 1"
|
|
307
|
+
.config=${{ ...ZERO_CONFIG, columns, rows: { ...ZERO_CONFIG.rows } }}
|
|
308
|
+
.data=${{ total: 1, records: [{ palletId: 'P-0001', location: 'A-01' }] }}
|
|
309
|
+
></ox-grid>
|
|
310
|
+
</div>
|
|
311
|
+
`);
|
|
312
|
+
const grid = el.querySelector('ox-grid');
|
|
313
|
+
await grid.updateComplete;
|
|
314
|
+
const wrapper = grid.renderRoot.querySelector('[role=grid]');
|
|
315
|
+
const header = grid.renderRoot.querySelector('ox-grid-header');
|
|
316
|
+
const body = grid.renderRoot.querySelector('ox-grid-body');
|
|
317
|
+
expect(getComputedStyle(wrapper).display).to.equal('contents');
|
|
318
|
+
/* 몸이 남는 높이를 다 쓴다 — 감싸개가 상자를 만들면 이 값이 머리 높이만큼으로 줄어든다 */
|
|
319
|
+
expect(body.offsetHeight, '몸이 남는 높이를 쓰지 못한다').to.be.greaterThan(100);
|
|
320
|
+
expect(body.offsetTop, '몸이 머리 아래에 있지 않다').to.be.greaterThan(0);
|
|
321
|
+
expect(header.offsetTop).to.equal(0);
|
|
322
|
+
});
|
|
323
|
+
});
|
|
324
|
+
//# sourceMappingURL=accessible-name.test.js.map
|
|
@@ -0,0 +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"]}
|
|
@@ -10,7 +10,7 @@ import { DataGrid } from '../src/data-grid/data-grid.js';
|
|
|
10
10
|
* 줄과 띠가 나란히 안 보여도 그 시험은 전부 초록이다. 실제로 그랬다.
|
|
11
11
|
*
|
|
12
12
|
* ```
|
|
13
|
-
* [
|
|
13
|
+
* [확인함] 2026-09-04 · operato-plant 인원 화면(어두운 모드)
|
|
14
14
|
* 줄 rgb(30 34 40) · 띠 rgb(26 30 35) → 대비 1.048 보이지 않는다
|
|
15
15
|
* 그때 고정 열 시험 열여섯 건은 전부 초록이었다
|
|
16
16
|
* ```
|
|
@@ -103,7 +103,7 @@ const 팔레트들 = {
|
|
|
103
103
|
* 얼마나 벌어져야 보이나.
|
|
104
104
|
*
|
|
105
105
|
* 지나는 줄 표시는 **훨씬 옅어야 한다** — 고른 줄과 같은 무게로 그리면 무엇이 골라졌는지 흐려진다.
|
|
106
|
-
* 그래서 큰 값을 요구하지 않는다. 다만 1.05 는 눈에 안 들어온다(위 [
|
|
106
|
+
* 그래서 큰 값을 요구하지 않는다. 다만 1.05 는 눈에 안 들어온다(위 [확인함]). 눈으로 확인한 값이
|
|
107
107
|
* 1.11 · 1.14 이므로 그 아래에 문턱을 둔다.
|
|
108
108
|
*/
|
|
109
109
|
const 최소분리 = 1.08;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"row-hover-visible.test.js","sourceRoot":"","sources":["../../test/row-hover-visible.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { expect } from '@open-wc/testing'\n\nimport { DataGrist } from '../src/data-grist.js'\nimport { DataGrid } from '../src/data-grid/data-grid.js'\n\n/**\n * 마우스가 지나는 줄이 **보이는가.**\n *\n * ## 왜 이 시험이 따로 있나\n *\n * `fixed-column-opaque.test.ts` 는 색이 **같은지**를 잰다. 그것만으로는 「띠가 보인다」를 못 지킨다 —\n * 줄과 띠가 나란히 안 보여도 그 시험은 전부 초록이다. 실제로 그랬다.\n *\n * ```\n * [
|
|
1
|
+
{"version":3,"file":"row-hover-visible.test.js","sourceRoot":"","sources":["../../test/row-hover-visible.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { expect } from '@open-wc/testing'\n\nimport { DataGrist } from '../src/data-grist.js'\nimport { DataGrid } from '../src/data-grid/data-grid.js'\n\n/**\n * 마우스가 지나는 줄이 **보이는가.**\n *\n * ## 왜 이 시험이 따로 있나\n *\n * `fixed-column-opaque.test.ts` 는 색이 **같은지**를 잰다. 그것만으로는 「띠가 보인다」를 못 지킨다 —\n * 줄과 띠가 나란히 안 보여도 그 시험은 전부 초록이다. 실제로 그랬다.\n *\n * ```\n * [확인함] 2026-09-04 · operato-plant 인원 화면(어두운 모드)\n * 줄 rgb(30 34 40) · 띠 rgb(26 30 35) → 대비 1.048 보이지 않는다\n * 그때 고정 열 시험 열여섯 건은 전부 초록이었다\n * ```\n *\n * 원인은 색이 아니라 **어디서 색을 가져오나**였다. 면 사다리에서 한 단 옮겨 오면, 그 사다리의\n * 밝기 순서가 앱마다 맞아야 한다. operato-plant 의 어두운 팔레트는 그 순서가 밝은 모드와 같아서\n * 띠가 줄보다 어두워지는 쪽으로 4단 움직였을 뿐이다.\n *\n * 그래서 지금은 **줄 자신의 색에서 만든다.** 이 시험은 그 규칙이 두 테마에서 다 서는지를 잰다.\n */\n\n/** 스타일 뭉치에서 그 토큰의 선언값을 꺼낸다 — 시험이 값을 베끼지 않게. */\nfunction 선언값(styles: unknown, token: string) {\n const cssText = (Array.isArray(styles) ? styles : [styles])\n .map(style => String((style as { cssText?: string })?.cssText ?? style))\n .join('\\n')\n\n /* 여러 줄에 걸친 color-mix(...) 도 담기게 다음 세미콜론까지 가져온다. */\n const found = cssText.match(new RegExp(`${token}\\\\s*:\\\\s*([^;]+);`))\n\n return found?.[1].replace(/\\s+/g, ' ').trim()\n}\n\n/**\n * 색 문자열을 0~255 세 값으로 만든다 — **캔버스에 칠해서 읽는다.**\n *\n * 문자열을 직접 파싱하면 안 된다. `color-mix` 는 계산되면 `color(srgb 0.954 0.957 0.958)` 로\n * 나오고, 그 숫자를 0~255 로 읽으면 거의 검정이 된다. 실제로 그렇게 한 번 재서 이 시험이\n * 「대비 20.9」라는 헛것을 냈다. 캔버스는 어떤 표기든 브라우저가 칠한 픽셀로 돌려준다.\n */\nconst 칠 = document.createElement('canvas').getContext('2d', { willReadFrequently: true })!\n\nfunction 픽셀(color: string) {\n 칠.clearRect(0, 0, 1, 1)\n 칠.fillStyle = '#000'\n 칠.fillStyle = color\n 칠.fillRect(0, 0, 1, 1)\n\n const [r, g, b] = 칠.getImageData(0, 0, 1, 1).data\n\n return [r, g, b]\n}\n\nconst lum = (color: string) => {\n const [r, g, b] = 픽셀(color)\n const f = (v: number) => {\n const x = v / 255\n return x <= 0.03928 ? x / 12.92 : Math.pow((x + 0.055) / 1.055, 2.4)\n }\n\n return 0.2126 * f(r) + 0.7152 * f(g) + 0.0722 * f(b)\n}\n\nconst 대비 = (a: string, b: string) => {\n const [x, y] = [lum(a), lum(b)].sort((p, q) => q - p)\n return (x + 0.05) / (y + 0.05)\n}\n\n/**\n * 그 팔레트에서 선언값이 실제로 무슨 색이 되는지 — 브라우저에게 계산시킨다.\n *\n * ── 기다리지 않는다 (2026-09-05) ──────────────────────────────────────────\n * 전에는 fixture() 로 세우고 await 했다. 잴 것은 div 하나의 계산된 색뿐이라 렌더링을 기다릴\n * 이유가 없는데, 그 await 가 mocha 의 2초 문턱에 걸려 **기계가 바쁠 때만 붉어졌다** —\n * 빌드와 웹팩이 함께 돌던 동안 어두운 팔레트 두 건이 시간초과로 넘어갔고, 혼자 돌리면 초록이었다.\n * 색 판정은 한 번도 실패한 적이 없다.\n *\n * 붙이고 재고 떼는 것으로 충분하다. 기다림이 없으니 부하와 무관하게 같은 답이 나온다.\n */\nfunction 칠해보다(값: string, 팔레트: { 줄: string; 잉크: string; 사다리: string }) {\n const probe = document.createElement('div')\n\n probe.setAttribute(\n 'style',\n `${팔레트.사다리} --grid-record-background-color:${팔레트.줄}; --grid-record-color:${팔레트.잉크}; background-color:${값};`\n )\n\n document.body.appendChild(probe)\n\n try {\n return getComputedStyle(probe).backgroundColor\n } finally {\n probe.remove()\n }\n}\n\n/**\n * operato-plant 의 두 팔레트에서 실제로 쓰는 값이다.\n *\n * 면 사다리도 함께 준다 — 선언이 사다리에서 색을 가져오는 방식으로 되돌아가도 **그 앱에서 실제로\n * 나오는 색**으로 재기 위해서다. 주지 않으면 리터럴 폴백으로 떨어져 두 테마가 같은 색이 되고,\n * 어두운 모드에서 띠가 사라지던 그 일을 이 시험이 못 잡는다.\n */\nconst 팔레트들 = {\n 밝은: {\n 줄: 'rgb(255, 255, 255)',\n 잉크: 'rgb(23, 36, 44)',\n 사다리: '--md-sys-color-surface-container-lowest: rgb(255,255,255); --md-sys-color-surface-container-low: rgb(247,248,250);'\n },\n 어두운: {\n 줄: 'rgb(30, 34, 40)',\n 잉크: 'rgb(228, 232, 236)',\n 사다리: '--md-sys-color-surface-container-lowest: rgb(30,34,40); --md-sys-color-surface-container-low: rgb(26,30,35);'\n }\n}\n\n/**\n * 얼마나 벌어져야 보이나.\n *\n * 지나는 줄 표시는 **훨씬 옅어야 한다** — 고른 줄과 같은 무게로 그리면 무엇이 골라졌는지 흐려진다.\n * 그래서 큰 값을 요구하지 않는다. 다만 1.05 는 눈에 안 들어온다(위 [확인함]). 눈으로 확인한 값이\n * 1.11 · 1.14 이므로 그 아래에 문턱을 둔다.\n */\nconst 최소분리 = 1.08\n\ndescribe('마우스가 지나는 줄이 보인다', () => {\n const 값 = 선언값(DataGrist.styles, '--grid-record-hover-background-color')\n\n it('그 토큰이 선언되어 있다 — 이름이 바뀌면 아래 시험들이 헛돈다', () => {\n expect(값, '--grid-record-hover-background-color 를 못 찾았다').to.be.a('string')\n })\n\n Object.entries(팔레트들).forEach(([이름, 팔레트]) => {\n it(`${이름} 팔레트에서 줄과 띠가 갈린다`, () => {\n const 띠 = 칠해보다(값!, 팔레트)\n const 벌어짐 = 대비(띠, 팔레트.줄)\n\n expect(\n 벌어짐,\n `줄 ${팔레트.줄} · 띠 ${픽셀(띠).join(' ')} → 대비 ${벌어짐.toFixed(3)}. 이만큼이면 마우스가 어느 줄에 있는지 안 보인다`\n ).to.be.greaterThan(최소분리)\n })\n\n it(`${이름} 팔레트에서 띠가 줄을 덮지 않는다 — 옅어야 한다`, () => {\n const 띠 = 칠해보다(값!, 팔레트)\n\n expect(대비(띠, 팔레트.줄), '고른 줄과 같은 무게가 되면 무엇이 골라졌는지 흐려진다').to.be.lessThan(1.6)\n })\n })\n\n /**\n * **줄 상태의 색을 `transparent` 로 두지 않는다.**\n *\n * 그 값은 「칠하지 않는다」가 아니라 「칸의 바탕을 지운다」다. 지워진 자리에는 표 뒤에 있는 것이\n * 비치고, 그것은 표가 정한 색이 아니다 — 어두운 페이지에 표를 놓으면 지나는 줄이 검게 된다.\n * 그리고 고정 열은 불투명해야 하므로 구멍이 안 뚫려, 줄 하나가 두 색으로 갈린다.\n *\n * 실제로 고치는 표가 그렇게 두고 있었다. 앞의 시험들은 **자기가 세운 표에 자기가 값을 넣으므로**\n * 이것을 못 잡는다 — 그 줄이 되돌아와도 초록이다. 그래서 선언 자체를 본다.\n */\n ;[\n ['DataGrist', DataGrist.styles],\n ['DataGrid', DataGrid.styles]\n ].forEach(([이름, styles]) => {\n it(`${이름} 이 줄 상태의 색을 비우지 않는다`, () => {\n const cssText = (styles as unknown[])\n .map(style => String((style as { cssText?: string })?.cssText ?? style))\n .join('\\n')\n\n const 비운것 = cssText.match(/--grid-record-[a-z-]*background-color\\s*:\\s*transparent/g)\n\n expect(비운것, `${비운것?.join(' · ')} — 비우는 대신 그 줄의 색을 주어야 한다`).to.be.null\n })\n })\n})\n"]}
|