@operato/data-grist 10.9.0 → 10.10.1
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 +24 -0
- package/dist/src/configure/column-builder.js +16 -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 +125 -40
- 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 +82 -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 +16 -1
- package/dist/src/editors/ox-grist-editor.js +29 -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 +16 -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 +322 -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 +406 -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
|
+
* The column's name as plain text.
|
|
3
|
+
*
|
|
4
|
+
* A header renderer returns a lit template, so the name cannot be read back out of it. Three places
|
|
5
|
+
* need the name outside of rendering:
|
|
6
|
+
*
|
|
7
|
+
* screen readers an input inside a cell has to say which column it edits
|
|
8
|
+
* header filters the boxes under the header row had no name at all
|
|
9
|
+
* the IME buffer which cell the keyboard is currently on
|
|
10
|
+
*
|
|
11
|
+
* This is the single source. Callers must not spell the name out again — then a change to `header`
|
|
12
|
+
* leaves the old name behind everywhere else.
|
|
13
|
+
*
|
|
14
|
+
* Lookup order matches what a sighted reader sees: header, then label, then `column.name`.
|
|
15
|
+
* `column.name` is a developer identifier and may be meaningless, but it beats no 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 * The column's name as plain text.\n *\n * A header renderer returns a lit template, so the name cannot be read back out of it. Three places\n * need the name outside of rendering:\n *\n * screen readers an input inside a cell has to say which column it edits\n * header filters the boxes under the header row had no name at all\n * the IME buffer which cell the keyboard is currently on\n *\n * This is the single source. Callers must not spell the name out again — then a change to `header`\n * leaves the old name behind everywhere else.\n *\n * Lookup order matches what a sighted reader sees: header, then label, then `column.name`.\n * `column.name` is a developer identifier and may be meaningless, but it beats no 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,322 @@
|
|
|
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
|
+
* Accessible names on the inputs inside the grid.
|
|
10
|
+
*
|
|
11
|
+
* ## Why this is tested
|
|
12
|
+
*
|
|
13
|
+
* Several inputs here had no name: the row-select checkboxes, the header filter boxes, and the IME
|
|
14
|
+
* buffer that holds focus. Position carries the meaning for a sighted reader — a narrow leftmost
|
|
15
|
+
* column means row select, a box under a header name filters that column — but position is a
|
|
16
|
+
* picture, not a relationship, so nothing of it reaches a screen reader.
|
|
17
|
+
*
|
|
18
|
+
* ## What has to hold
|
|
19
|
+
*
|
|
20
|
+
* The name has one source. Editing `header` must change what is announced, which means callers
|
|
21
|
+
* cannot spell the name out again. `columnText()` is that source.
|
|
22
|
+
*
|
|
23
|
+
* `buildColumn` turns a string header into `() => header`, and that function returns a lit template
|
|
24
|
+
* the name cannot be read back out of. The name was disappearing right there.
|
|
25
|
+
*/
|
|
26
|
+
describe('column name has one source', () => {
|
|
27
|
+
it('keeps the plain text after a string header becomes a function', () => {
|
|
28
|
+
const column = buildColumn({ type: 'string', name: 'palletId', header: '파렛트 번호' });
|
|
29
|
+
expect(columnText(column)).to.equal('파렛트 번호');
|
|
30
|
+
});
|
|
31
|
+
it('keeps it when the string arrives as header.renderer', () => {
|
|
32
|
+
const column = buildColumn({ type: 'string', name: 'palletId', header: { renderer: '파렛트 번호' } });
|
|
33
|
+
expect(columnText(column)).to.equal('파렛트 번호');
|
|
34
|
+
});
|
|
35
|
+
it('lets label: true inherit the header name', () => {
|
|
36
|
+
const column = buildColumn({ type: 'string', name: 'palletId', header: '파렛트 번호', label: true });
|
|
37
|
+
expect(columnText(column)).to.equal('파렛트 번호');
|
|
38
|
+
});
|
|
39
|
+
it('falls back to column.name when the header is drawn by a function', () => {
|
|
40
|
+
const column = buildColumn({ type: 'string', name: 'palletId', header: { renderer: () => html `<i>x</i>` } });
|
|
41
|
+
expect(columnText(column)).to.equal('palletId');
|
|
42
|
+
});
|
|
43
|
+
it('invents no name when there is no column', () => {
|
|
44
|
+
expect(columnText(undefined)).to.equal('');
|
|
45
|
+
expect(columnText(null)).to.equal('');
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
describe('row-select boxes are named', () => {
|
|
49
|
+
const columns = [
|
|
50
|
+
{ type: 'gutter', gutterName: 'row-selector', name: '__selected__', multiple: true },
|
|
51
|
+
{ type: 'string', name: 'palletId', header: '파렛트 번호' }
|
|
52
|
+
].map(buildColumn);
|
|
53
|
+
it('says which row each checkbox selects', 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
|
+
/* The checkboxes live one shadow root deeper, inside each ox-grid-field. */
|
|
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, 'found no row-select box').to.be.greaterThan(0);
|
|
67
|
+
boxes.forEach(box => expect(box.getAttribute('aria-label'), 'an unnamed checkbox is left').to.be.a('string').and.not.equal(''));
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
describe('IME buffer names the cell it stands in for', () => {
|
|
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('reads the first column while focus sits there', async () => {
|
|
88
|
+
expect((await bodyFocusedAt(0)).getAttribute('aria-label')).to.contain('파렛트 번호');
|
|
89
|
+
});
|
|
90
|
+
it('changes what it reads when focus moves a column over', async () => {
|
|
91
|
+
expect((await bodyFocusedAt(1)).getAttribute('aria-label')).to.contain('보관 위치');
|
|
92
|
+
});
|
|
93
|
+
it('stays visible to assistive tech — it is where focus lands', async () => {
|
|
94
|
+
expect((await bodyFocusedAt(0)).getAttribute('aria-hidden')).to.be.null;
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
/**
|
|
98
|
+
* Table semantics.
|
|
99
|
+
*
|
|
100
|
+
* ## Why this is tested
|
|
101
|
+
*
|
|
102
|
+
* The grid is drawn with CSS grid: cells sit flat and there is no row element. Rows are a picture
|
|
103
|
+
* made of equal offsetTop, so a screen reader saw a pile of inputs rather than a table — no "this
|
|
104
|
+
* is a table", no "row 3 column 3", no "40 rows in all".
|
|
105
|
+
*
|
|
106
|
+
* Adding row wrappers can break two things at once:
|
|
107
|
+
*
|
|
108
|
+
* ```
|
|
109
|
+
* semantics grid / row / columnheader / gridcell have to nest correctly
|
|
110
|
+
* layout display: contents must generate no box; if it does, the columns collapse
|
|
111
|
+
* ```
|
|
112
|
+
*
|
|
113
|
+
* The second one is why geometry is asserted: cells in a row must step right, and rows must step
|
|
114
|
+
* down.
|
|
115
|
+
*/
|
|
116
|
+
describe('grid declares itself a grid', () => {
|
|
117
|
+
/* ox-grid takes already-compiled columns; compiling them is ox-grist's job. */
|
|
118
|
+
const columns = [
|
|
119
|
+
{ type: 'gutter', gutterName: 'row-selector', name: '__selected__', multiple: true },
|
|
120
|
+
{ type: 'string', name: 'palletId', header: '파렛트 번호', sortable: true },
|
|
121
|
+
{ type: 'string', name: 'location', header: '보관 위치', sortable: true }
|
|
122
|
+
].map(buildColumn);
|
|
123
|
+
async function grid(overrides = {}) {
|
|
124
|
+
const el = await fixture(html `
|
|
125
|
+
<ox-grid
|
|
126
|
+
aria-label="입고 대기 목록"
|
|
127
|
+
.config=${{ ...ZERO_CONFIG, columns, rows: { ...ZERO_CONFIG.rows } }}
|
|
128
|
+
.data=${{
|
|
129
|
+
page: 1,
|
|
130
|
+
limit: 20,
|
|
131
|
+
total: 40,
|
|
132
|
+
records: [
|
|
133
|
+
{ palletId: 'P-0001', location: 'A-01' },
|
|
134
|
+
{ palletId: 'P-0002', location: 'A-02' }
|
|
135
|
+
],
|
|
136
|
+
...overrides
|
|
137
|
+
}}
|
|
138
|
+
></ox-grid>
|
|
139
|
+
`);
|
|
140
|
+
await el.updateComplete;
|
|
141
|
+
const header = el.renderRoot.querySelector('ox-grid-header');
|
|
142
|
+
const body = el.renderRoot.querySelector('ox-grid-body');
|
|
143
|
+
await (header === null || header === void 0 ? void 0 : header.updateComplete);
|
|
144
|
+
await (body === null || body === void 0 ? void 0 : body.updateComplete);
|
|
145
|
+
return { el, header, body };
|
|
146
|
+
}
|
|
147
|
+
it('nests grid, row and cell', async () => {
|
|
148
|
+
const { el, header, body } = await grid();
|
|
149
|
+
const grid_ = el.renderRoot.querySelector('[role=grid]');
|
|
150
|
+
expect(grid_, 'no role=grid').to.exist;
|
|
151
|
+
expect(header.getAttribute('role')).to.equal('rowgroup');
|
|
152
|
+
expect(body.getAttribute('role')).to.equal('rowgroup');
|
|
153
|
+
expect(header.renderRoot.querySelectorAll('[role=row]').length).to.equal(1);
|
|
154
|
+
expect(body.renderRoot.querySelectorAll('[role=row]').length).to.equal(2);
|
|
155
|
+
expect(header.renderRoot.querySelectorAll('[role=columnheader]').length).to.equal(3);
|
|
156
|
+
});
|
|
157
|
+
it('counts the whole table, not the page on screen', async () => {
|
|
158
|
+
const { el } = await grid();
|
|
159
|
+
const grid_ = el.renderRoot.querySelector('[role=grid]');
|
|
160
|
+
/* One header row plus 40 records. Two rows drawn is still a 40-row table. */
|
|
161
|
+
expect(grid_.getAttribute('aria-rowcount')).to.equal('41');
|
|
162
|
+
expect(grid_.getAttribute('aria-colcount')).to.equal('3');
|
|
163
|
+
expect(grid_.getAttribute('aria-label')).to.equal('입고 대기 목록');
|
|
164
|
+
});
|
|
165
|
+
it('numbers page 2 from row 22, not back at 1', async () => {
|
|
166
|
+
const { body } = await grid({ page: 2 });
|
|
167
|
+
const rows = Array.from(body.renderRoot.querySelectorAll('[role=row]'));
|
|
168
|
+
/* One header row plus 20 records on page 1. */
|
|
169
|
+
expect(rows[0].getAttribute('aria-rowindex')).to.equal('22');
|
|
170
|
+
expect(rows[1].getAttribute('aria-rowindex')).to.equal('23');
|
|
171
|
+
});
|
|
172
|
+
it('numbers columns and leaves the width filler uncounted', async () => {
|
|
173
|
+
const { body } = await grid();
|
|
174
|
+
const row = body.renderRoot.querySelector('[role=row]');
|
|
175
|
+
const cells = Array.from(row.querySelectorAll('[role=gridcell]'));
|
|
176
|
+
expect(cells.map(cell => cell.getAttribute('aria-colindex'))).to.deep.equal(['1', '2', '3']);
|
|
177
|
+
expect(row.querySelectorAll('[role=presentation]').length, 'the filler was counted as a cell').to.equal(1);
|
|
178
|
+
});
|
|
179
|
+
it('states sort direction in text, not only as an arrow', async () => {
|
|
180
|
+
const el = await fixture(html `
|
|
181
|
+
<ox-grid
|
|
182
|
+
.config=${{ ...ZERO_CONFIG, columns, rows: { ...ZERO_CONFIG.rows } }}
|
|
183
|
+
.sorters=${[{ name: 'palletId', desc: true }]}
|
|
184
|
+
.data=${{ records: [{ palletId: 'P-0001', location: 'A-01' }] }}
|
|
185
|
+
></ox-grid>
|
|
186
|
+
`);
|
|
187
|
+
await el.updateComplete;
|
|
188
|
+
const header = el.renderRoot.querySelector('ox-grid-header');
|
|
189
|
+
await header.updateComplete;
|
|
190
|
+
const headers = Array.from(header.renderRoot.querySelectorAll('[role=columnheader]'));
|
|
191
|
+
const sorts = headers.map(cell => cell.getAttribute('aria-sort'));
|
|
192
|
+
/* A gutter cannot be sorted; claiming otherwise announces a control that is not there. */
|
|
193
|
+
expect(sorts[0]).to.be.null;
|
|
194
|
+
expect(sorts[1], 'sorted descending').to.equal('descending');
|
|
195
|
+
expect(sorts[2], 'sortable but not currently sorted').to.equal('none');
|
|
196
|
+
});
|
|
197
|
+
it('keeps the layout — a row wrapper that generates a box collapses the grid', async () => {
|
|
198
|
+
const { body } = await grid();
|
|
199
|
+
const rows = Array.from(body.renderRoot.querySelectorAll('[role=row]'));
|
|
200
|
+
const first = Array.from(rows[0].children);
|
|
201
|
+
expect(getComputedStyle(rows[0]).display).to.equal('contents');
|
|
202
|
+
/* Cells step right; stacked at one x means the columns are gone. */
|
|
203
|
+
const lefts = first.map(cell => cell.offsetLeft);
|
|
204
|
+
expect(new Set(lefts).size, 'cells stacked at the same x').to.be.greaterThan(1);
|
|
205
|
+
lefts.slice(1).forEach((left, index) => expect(left).to.be.greaterThan(lefts[index]));
|
|
206
|
+
/* Rows step down. */
|
|
207
|
+
const firstTop = rows[0].children[0].offsetTop;
|
|
208
|
+
const secondTop = rows[1].children[0].offsetTop;
|
|
209
|
+
expect(secondTop, 'two rows at the same y').to.be.greaterThan(firstTop);
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
/**
|
|
213
|
+
* Grouped headers.
|
|
214
|
+
*
|
|
215
|
+
* The header becomes two rows and ungrouped columns span both (`.span-both`). Placement runs
|
|
216
|
+
* through CSS `grid-row` / `grid-column`, which makes this the riskiest spot for a row wrapper.
|
|
217
|
+
*/
|
|
218
|
+
describe('grouped header spans two rows', () => {
|
|
219
|
+
const columns = [
|
|
220
|
+
{ type: 'string', name: 'palletId', header: '파렛트 번호' },
|
|
221
|
+
{ type: 'string', name: 'width', header: { renderer: '폭', group: '치수' } },
|
|
222
|
+
{ type: 'string', name: 'height', header: { renderer: '높이', group: '치수' } }
|
|
223
|
+
].map(buildColumn);
|
|
224
|
+
async function grouped() {
|
|
225
|
+
const el = await fixture(html `
|
|
226
|
+
<ox-grid
|
|
227
|
+
.config=${{ ...ZERO_CONFIG, columns, rows: { ...ZERO_CONFIG.rows } }}
|
|
228
|
+
.data=${{ total: 1, records: [{ palletId: 'P-0001', width: '1200', height: '900' }] }}
|
|
229
|
+
></ox-grid>
|
|
230
|
+
`);
|
|
231
|
+
await el.updateComplete;
|
|
232
|
+
const header = el.renderRoot.querySelector('ox-grid-header');
|
|
233
|
+
const body = el.renderRoot.querySelector('ox-grid-body');
|
|
234
|
+
await header.updateComplete;
|
|
235
|
+
await body.updateComplete;
|
|
236
|
+
return { el, header, body };
|
|
237
|
+
}
|
|
238
|
+
it('counts two header rows so the body starts at row 3', async () => {
|
|
239
|
+
const { el, header, body } = await grouped();
|
|
240
|
+
expect(header.renderRoot.querySelectorAll('[role=row]').length).to.equal(2);
|
|
241
|
+
/* Two header rows plus one record. */
|
|
242
|
+
expect(el.renderRoot.querySelector('[role=grid]').getAttribute('aria-rowcount')).to.equal('3');
|
|
243
|
+
const rows = Array.from(body.renderRoot.querySelectorAll('[role=row]'));
|
|
244
|
+
expect(rows[0].getAttribute('aria-rowindex')).to.equal('3');
|
|
245
|
+
});
|
|
246
|
+
it('says how many columns a group covers', async () => {
|
|
247
|
+
const { header } = await grouped();
|
|
248
|
+
const cells = Array.from(header.renderRoot.querySelectorAll('[role=columnheader]'));
|
|
249
|
+
const first = cells[0];
|
|
250
|
+
const group = cells.find(cell => cell.getAttribute('aria-colspan'));
|
|
251
|
+
/* An ungrouped column spans both header rows. */
|
|
252
|
+
expect(first.getAttribute('aria-rowspan')).to.equal('2');
|
|
253
|
+
expect(first.getAttribute('aria-colindex')).to.equal('1');
|
|
254
|
+
/* The group header covers columns 2 and 3. */
|
|
255
|
+
expect(group, 'found no group header').to.exist;
|
|
256
|
+
expect(group.getAttribute('aria-colindex')).to.equal('2');
|
|
257
|
+
expect(group.getAttribute('aria-colspan')).to.equal('2');
|
|
258
|
+
});
|
|
259
|
+
it('keeps two-row header placement — group sits above its leaf columns', async () => {
|
|
260
|
+
const { header } = await grouped();
|
|
261
|
+
const cells = Array.from(header.renderRoot.querySelectorAll('[role=columnheader]'));
|
|
262
|
+
const group = cells.find(cell => cell.getAttribute('aria-colspan'));
|
|
263
|
+
const leaf = cells.find(cell => !cell.getAttribute('aria-colspan') && cell !== cells[0]);
|
|
264
|
+
expect(leaf.offsetTop, 'group header and its leaf column at the same height').to.be.greaterThan(group.offsetTop);
|
|
265
|
+
expect(group.offsetWidth, 'group header does not cover two columns').to.be.greaterThan(leaf.offsetWidth);
|
|
266
|
+
});
|
|
267
|
+
});
|
|
268
|
+
/**
|
|
269
|
+
* The grid wrapper must not disturb the surrounding layout.
|
|
270
|
+
*
|
|
271
|
+
* `role=grid` admits only rows, so the settings-button slot and the pager moved outside the
|
|
272
|
+
* wrapper. Two things then depend on this structure:
|
|
273
|
+
*
|
|
274
|
+
* ```
|
|
275
|
+
* column layout the host is a flex column. A wrapper that generates a box swallows header
|
|
276
|
+
* and body into one flex item, and the body's flex: 1 stops working
|
|
277
|
+
* sibling selector "show the settings button on header hover" is an adjacent-sibling rule, so
|
|
278
|
+
* the button has to sit directly after the wrapper
|
|
279
|
+
* ```
|
|
280
|
+
*/
|
|
281
|
+
describe('grid wrapper stays out of the layout', () => {
|
|
282
|
+
const columns = [
|
|
283
|
+
{ type: 'string', name: 'palletId', header: '파렛트 번호' },
|
|
284
|
+
{ type: 'string', name: 'location', header: '보관 위치' }
|
|
285
|
+
].map(buildColumn);
|
|
286
|
+
it('places the settings button after the grid, not inside it', async () => {
|
|
287
|
+
const el = await fixture(html `
|
|
288
|
+
<ox-grid
|
|
289
|
+
.config=${{ ...ZERO_CONFIG, columns, rows: { ...ZERO_CONFIG.rows } }}
|
|
290
|
+
.data=${{ total: 1, records: [{ palletId: 'P-0001', location: 'A-01' }] }}
|
|
291
|
+
></ox-grid>
|
|
292
|
+
`);
|
|
293
|
+
await el.updateComplete;
|
|
294
|
+
const grid = el.renderRoot.querySelector('[role=grid]');
|
|
295
|
+
const setting = el.renderRoot.querySelector('div[setting]');
|
|
296
|
+
expect(setting, 'no settings slot').to.exist;
|
|
297
|
+
expect(grid.contains(setting), 'settings button inside the grid breaks table reading').to.be.false;
|
|
298
|
+
expect(grid.nextElementSibling, 'adjacent-sibling rule no longer holds').to.equal(setting);
|
|
299
|
+
});
|
|
300
|
+
it('leaves header and body as flex items of the host', async () => {
|
|
301
|
+
const el = await fixture(html `
|
|
302
|
+
<div style="height: 300px; display: flex">
|
|
303
|
+
<ox-grid
|
|
304
|
+
style="flex: 1"
|
|
305
|
+
.config=${{ ...ZERO_CONFIG, columns, rows: { ...ZERO_CONFIG.rows } }}
|
|
306
|
+
.data=${{ total: 1, records: [{ palletId: 'P-0001', location: 'A-01' }] }}
|
|
307
|
+
></ox-grid>
|
|
308
|
+
</div>
|
|
309
|
+
`);
|
|
310
|
+
const grid = el.querySelector('ox-grid');
|
|
311
|
+
await grid.updateComplete;
|
|
312
|
+
const wrapper = grid.renderRoot.querySelector('[role=grid]');
|
|
313
|
+
const header = grid.renderRoot.querySelector('ox-grid-header');
|
|
314
|
+
const body = grid.renderRoot.querySelector('ox-grid-body');
|
|
315
|
+
expect(getComputedStyle(wrapper).display).to.equal('contents');
|
|
316
|
+
/* The body takes the remaining height; a wrapper with a box shrinks it to header height. */
|
|
317
|
+
expect(body.offsetHeight, 'body does not take the remaining height').to.be.greaterThan(100);
|
|
318
|
+
expect(body.offsetTop, 'body is not below the header').to.be.greaterThan(0);
|
|
319
|
+
expect(header.offsetTop).to.equal(0);
|
|
320
|
+
});
|
|
321
|
+
});
|
|
322
|
+
//# 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 * 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"]}
|
|
@@ -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"]}
|