@digital-realty/ix-grid 1.4.1-alpha.1 → 1.4.1-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ix-grid.min.js +5 -5
- package/package.json +4 -4
- package/rollup.config.mjs +32 -6
- package/src/IxGrid.d.ts +148 -0
- package/src/IxGrid.js +784 -0
- package/src/IxGrid.js.map +1 -0
- package/src/IxGridNav.d.ts +2 -0
- package/src/IxGridNav.js +8 -0
- package/src/IxGridNav.js.map +1 -0
- package/src/components/IxGridColumnFilter.d.ts +35 -0
- package/src/components/IxGridColumnFilter.js +249 -0
- package/src/components/IxGridColumnFilter.js.map +1 -0
- package/src/components/IxGridDownloadMenu.d.ts +17 -0
- package/src/components/IxGridDownloadMenu.js +98 -0
- package/src/components/IxGridDownloadMenu.js.map +1 -0
- package/src/components/IxGridNav.d.ts +16 -0
- package/src/components/IxGridNav.js +103 -0
- package/src/components/IxGridNav.js.map +1 -0
- package/src/components/IxGridNoRows.d.ts +10 -0
- package/src/components/IxGridNoRows.js +74 -0
- package/src/components/IxGridNoRows.js.map +1 -0
- package/src/components/IxGridRowFilter.d.ts +55 -0
- package/src/components/IxGridRowFilter.js +441 -0
- package/src/components/IxGridRowFilter.js.map +1 -0
- package/src/components/IxPagination.d.ts +14 -0
- package/src/components/IxPagination.js +107 -0
- package/src/components/IxPagination.js.map +1 -0
- package/src/components/grid-column-filter-styles.d.ts +1 -0
- package/src/components/grid-column-filter-styles.js +89 -0
- package/src/components/grid-column-filter-styles.js.map +1 -0
- package/src/components/grid-row-filter-styles.d.ts +1 -0
- package/src/components/grid-row-filter-styles.js +311 -0
- package/src/components/grid-row-filter-styles.js.map +1 -0
- package/src/components/ix-grid-no-rows.d.ts +1 -0
- package/src/components/ix-grid-no-rows.js +2 -0
- package/src/components/ix-grid-no-rows.js.map +1 -0
- package/src/components/pagination-styles.d.ts +1 -0
- package/src/components/pagination-styles.js +84 -0
- package/src/components/pagination-styles.js.map +1 -0
- package/src/grid-view-styles.d.ts +1 -0
- package/src/grid-view-styles.js +283 -0
- package/src/grid-view-styles.js.map +1 -0
- package/src/index.d.ts +3 -0
- package/src/index.js +3 -0
- package/src/index.js.map +1 -0
- package/src/ix-grid-copy.d.ts +12 -0
- package/src/ix-grid-copy.js +12 -0
- package/src/ix-grid-copy.js.map +1 -0
- package/src/ix-grid-nav.d.ts +1 -0
- package/src/ix-grid-nav.js +2 -0
- package/src/ix-grid-nav.js.map +1 -0
- package/src/ix-grid-no-rows.d.ts +1 -0
- package/src/ix-grid-no-rows.js +2 -0
- package/src/ix-grid-no-rows.js.map +1 -0
- package/src/ix-grid.d.ts +1 -0
- package/src/ix-grid.js +2 -0
- package/src/ix-grid.js.map +1 -0
- package/src/models/IxGridDownloadMenuItemModel.d.ts +7 -0
- package/src/models/IxGridDownloadMenuItemModel.js +1 -0
- package/src/models/IxGridDownloadMenuItemModel.js.map +1 -0
- package/src/test/ix-grid-column-filter.test.js +41 -0
- package/src/test/ix-grid-row-filter.test.js +281 -0
- package/src/test/ix-grid.test.js +391 -0
- package/src/test/ix-pagination.test.js +58 -0
- package/web-test-runner.config.mjs +1 -1
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
/* eslint-disable no-restricted-globals */
|
|
2
|
+
import { aTimeout, elementUpdated, expect, fixture } from '@open-wc/testing';
|
|
3
|
+
import { html } from 'lit';
|
|
4
|
+
import sinon from 'sinon';
|
|
5
|
+
import { IxGridRowFilter } from '../components/IxGridRowFilter.js';
|
|
6
|
+
const columns = [
|
|
7
|
+
{
|
|
8
|
+
name: 'firstName',
|
|
9
|
+
header: 'First name',
|
|
10
|
+
filterable: true,
|
|
11
|
+
filterOperators: ['contains', 'equals'],
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
name: 'lastName',
|
|
15
|
+
header: 'Last name',
|
|
16
|
+
filterable: true,
|
|
17
|
+
filterOperators: ['contains', 'equals'],
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
name: 'middleName',
|
|
21
|
+
header: 'Middle name',
|
|
22
|
+
filterable: false,
|
|
23
|
+
filterOperators: ['contains', 'equals'],
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
name: 'email',
|
|
27
|
+
header: 'Email',
|
|
28
|
+
filterable: true,
|
|
29
|
+
filterOperators: ['contains', 'equals'],
|
|
30
|
+
},
|
|
31
|
+
];
|
|
32
|
+
const filters = [
|
|
33
|
+
{
|
|
34
|
+
columnField: 'firstName',
|
|
35
|
+
operatorValue: 'contains',
|
|
36
|
+
value: 'John',
|
|
37
|
+
},
|
|
38
|
+
];
|
|
39
|
+
describe('IxGridRowFilter', () => {
|
|
40
|
+
it('should render the grid row filter', async () => {
|
|
41
|
+
const el = await fixture(html `<ix-grid-row-filter .columns=${columns}></ix-grid-row-filter>`);
|
|
42
|
+
expect(el).to.be.instanceOf(IxGridRowFilter);
|
|
43
|
+
});
|
|
44
|
+
it('renders and popluates the grid row filter', async () => {
|
|
45
|
+
var _a, _b, _c;
|
|
46
|
+
const el = await fixture(html `<ix-grid-row-filter
|
|
47
|
+
.columns=${columns}
|
|
48
|
+
.filters=${filters}
|
|
49
|
+
></ix-grid-row-filter>`);
|
|
50
|
+
const openMenu = (_a = el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('.filter-button');
|
|
51
|
+
openMenu.click();
|
|
52
|
+
await elementUpdated(el);
|
|
53
|
+
const columnOptions = (_b = el.shadowRoot) === null || _b === void 0 ? void 0 : _b.querySelectorAll('select[data-v="firstName"] option');
|
|
54
|
+
expect(columnOptions === null || columnOptions === void 0 ? void 0 : columnOptions.length).to.equal(3);
|
|
55
|
+
const filterOperatorInput = (_c = el.shadowRoot) === null || _c === void 0 ? void 0 : _c.querySelector('select.filterOperatorInput');
|
|
56
|
+
filterOperatorInput.value = 'equals';
|
|
57
|
+
filterOperatorInput.dispatchEvent(new Event('change', {
|
|
58
|
+
bubbles: true,
|
|
59
|
+
cancelable: true,
|
|
60
|
+
}));
|
|
61
|
+
await elementUpdated(el);
|
|
62
|
+
const selectedOperator = filterOperatorInput.querySelector('option[selected]');
|
|
63
|
+
expect(selectedOperator === null || selectedOperator === void 0 ? void 0 : selectedOperator.value).to.equal('equals');
|
|
64
|
+
});
|
|
65
|
+
it('should debounce the filter input', async () => {
|
|
66
|
+
var _a, _b;
|
|
67
|
+
const debounceTime = 100;
|
|
68
|
+
const el = await fixture(html `<ix-grid-row-filter
|
|
69
|
+
.columns=${columns}
|
|
70
|
+
.filters=${filters}
|
|
71
|
+
.filterValueChangeDebounceTime=${debounceTime}
|
|
72
|
+
></ix-grid-row-filter>`);
|
|
73
|
+
const openMenu = (_a = el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('.filter-button');
|
|
74
|
+
openMenu.click();
|
|
75
|
+
await elementUpdated(el);
|
|
76
|
+
const filterInput = (_b = el.shadowRoot) === null || _b === void 0 ? void 0 : _b.querySelector('div.filterValueField input');
|
|
77
|
+
const debouncedOnFilterValueChangeSpy = sinon.spy(el, 'debouncedOnFilterValueChange');
|
|
78
|
+
const onFilterValueChangeSpy = sinon.spy(el, 'onfilterValueChange');
|
|
79
|
+
for (let i = 0; i < 5; i += 1) {
|
|
80
|
+
filterInput.dispatchEvent(new InputEvent('input', {
|
|
81
|
+
bubbles: true,
|
|
82
|
+
cancelable: true,
|
|
83
|
+
composed: true,
|
|
84
|
+
}));
|
|
85
|
+
}
|
|
86
|
+
expect(debouncedOnFilterValueChangeSpy).to.have.been.called;
|
|
87
|
+
expect(onFilterValueChangeSpy).to.not.have.been.called;
|
|
88
|
+
await aTimeout(debounceTime);
|
|
89
|
+
expect(onFilterValueChangeSpy).to.have.been.calledOnce;
|
|
90
|
+
});
|
|
91
|
+
describe('Filter type input', () => {
|
|
92
|
+
it('should render a string input control for non-supplied data type', async () => {
|
|
93
|
+
var _a, _b;
|
|
94
|
+
const columnsWithDataType = [
|
|
95
|
+
{
|
|
96
|
+
name: 'firstName',
|
|
97
|
+
header: 'First name',
|
|
98
|
+
filterable: true,
|
|
99
|
+
},
|
|
100
|
+
];
|
|
101
|
+
const el = await fixture(html `<ix-grid-row-filter
|
|
102
|
+
.columns=${columnsWithDataType}
|
|
103
|
+
></ix-grid-row-filter>`);
|
|
104
|
+
const openMenu = (_a = el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('.filter-button');
|
|
105
|
+
openMenu === null || openMenu === void 0 ? void 0 : openMenu.click();
|
|
106
|
+
await elementUpdated(el);
|
|
107
|
+
const filterInput = (_b = el.shadowRoot) === null || _b === void 0 ? void 0 : _b.querySelector('div.filterValueField input');
|
|
108
|
+
expect(filterInput).to.exist;
|
|
109
|
+
expect(filterInput).to.be.instanceOf(HTMLInputElement);
|
|
110
|
+
});
|
|
111
|
+
it('should render a string input control for string data type', async () => {
|
|
112
|
+
var _a, _b;
|
|
113
|
+
const columnsWithDataType = [
|
|
114
|
+
{
|
|
115
|
+
name: 'firstName',
|
|
116
|
+
header: 'First name',
|
|
117
|
+
filterable: true,
|
|
118
|
+
dataType: 'string',
|
|
119
|
+
},
|
|
120
|
+
];
|
|
121
|
+
const el = await fixture(html `<ix-grid-row-filter
|
|
122
|
+
.columns=${columnsWithDataType}
|
|
123
|
+
></ix-grid-row-filter>`);
|
|
124
|
+
const openMenu = (_a = el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('.filter-button');
|
|
125
|
+
openMenu === null || openMenu === void 0 ? void 0 : openMenu.click();
|
|
126
|
+
await elementUpdated(el);
|
|
127
|
+
const filterInput = (_b = el.shadowRoot) === null || _b === void 0 ? void 0 : _b.querySelector('div.filterValueField input');
|
|
128
|
+
expect(filterInput).to.exist;
|
|
129
|
+
expect(filterInput).to.be.instanceOf(HTMLInputElement);
|
|
130
|
+
});
|
|
131
|
+
it('should render a date input control for dateTime data type', async () => {
|
|
132
|
+
var _a, _b;
|
|
133
|
+
const columnsWithDataType = [
|
|
134
|
+
{
|
|
135
|
+
name: 'createdDate',
|
|
136
|
+
header: 'Created Date',
|
|
137
|
+
filterable: true,
|
|
138
|
+
dataType: 'dateTime',
|
|
139
|
+
},
|
|
140
|
+
];
|
|
141
|
+
const el = await fixture(html `<ix-grid-row-filter
|
|
142
|
+
.columns=${columnsWithDataType}
|
|
143
|
+
></ix-grid-row-filter>`);
|
|
144
|
+
const openMenu = (_a = el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('.filter-button');
|
|
145
|
+
openMenu === null || openMenu === void 0 ? void 0 : openMenu.click();
|
|
146
|
+
await elementUpdated(el);
|
|
147
|
+
const dateInput = (_b = el.shadowRoot) === null || _b === void 0 ? void 0 : _b.querySelector('div.filterValueField ix-date');
|
|
148
|
+
expect(dateInput).to.exist;
|
|
149
|
+
expect(dateInput).to.be.instanceOf(HTMLElement);
|
|
150
|
+
});
|
|
151
|
+
it('should render nothing when an unknown data type is used', async () => {
|
|
152
|
+
var _a, _b;
|
|
153
|
+
const columnsWithDataType = [
|
|
154
|
+
{
|
|
155
|
+
name: 'unknownTypeField',
|
|
156
|
+
header: 'Unknown Type',
|
|
157
|
+
filterable: true,
|
|
158
|
+
dataType: 'unknownType',
|
|
159
|
+
},
|
|
160
|
+
];
|
|
161
|
+
const el = await fixture(html `<ix-grid-row-filter
|
|
162
|
+
.columns=${columnsWithDataType}
|
|
163
|
+
></ix-grid-row-filter>`);
|
|
164
|
+
const openMenu = (_a = el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('.filter-button');
|
|
165
|
+
openMenu === null || openMenu === void 0 ? void 0 : openMenu.click();
|
|
166
|
+
await elementUpdated(el);
|
|
167
|
+
const inputField = (_b = el.shadowRoot) === null || _b === void 0 ? void 0 : _b.querySelector('div.filterValueField input');
|
|
168
|
+
expect(inputField).to.be.null;
|
|
169
|
+
});
|
|
170
|
+
it('should build filter from URL when readParamsFromURL is True', async () => {
|
|
171
|
+
var _a, _b, _c, _d, _e, _f;
|
|
172
|
+
const columnsWithDataType = [
|
|
173
|
+
{
|
|
174
|
+
name: 'firstName',
|
|
175
|
+
header: 'First name',
|
|
176
|
+
filterable: true,
|
|
177
|
+
dataType: 'string',
|
|
178
|
+
filterOperators: ['contains', 'equals'],
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
name: 'lastName',
|
|
182
|
+
header: 'Last Name',
|
|
183
|
+
filterable: true,
|
|
184
|
+
dataType: 'string',
|
|
185
|
+
filterOperators: ['equals'],
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
name: 'createdDate',
|
|
189
|
+
header: 'Created Date',
|
|
190
|
+
filterable: true,
|
|
191
|
+
dataType: 'dateTime',
|
|
192
|
+
filterOperators: ['equals'],
|
|
193
|
+
},
|
|
194
|
+
];
|
|
195
|
+
const el = (await fixture(html `<ix-grid-row-filter
|
|
196
|
+
.columns=${columnsWithDataType}
|
|
197
|
+
.readParamsFromURL=${true}
|
|
198
|
+
></ix-grid-row-filter>`));
|
|
199
|
+
const url = 'firstName_contains=John&createdDate_equals=2021-09-01';
|
|
200
|
+
history.pushState(null, '', `${location.pathname}?${url}`);
|
|
201
|
+
el.firstUpdated();
|
|
202
|
+
const openMenu = (_a = el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('.filter-button');
|
|
203
|
+
openMenu === null || openMenu === void 0 ? void 0 : openMenu.click();
|
|
204
|
+
await elementUpdated(el);
|
|
205
|
+
const formFilters = (_b = el.shadowRoot) === null || _b === void 0 ? void 0 : _b.querySelectorAll('.filter-form');
|
|
206
|
+
const filter1Name = (_c = formFilters[0]
|
|
207
|
+
.querySelector('.filterColumnField')
|
|
208
|
+
.querySelector('select')
|
|
209
|
+
.attributes.getNamedItem('data-v')) === null || _c === void 0 ? void 0 : _c.value;
|
|
210
|
+
const filter1Operator = (_d = formFilters[0]
|
|
211
|
+
.querySelector('.filterOperatorField')
|
|
212
|
+
.querySelector('select')
|
|
213
|
+
.attributes.getNamedItem('data-v')) === null || _d === void 0 ? void 0 : _d.value;
|
|
214
|
+
const filter1Value = formFilters[0]
|
|
215
|
+
.querySelector('.filterValueField')
|
|
216
|
+
.querySelector('input').value;
|
|
217
|
+
const filter2Name = (_e = formFilters[1]
|
|
218
|
+
.querySelector('.filterColumnField')
|
|
219
|
+
.querySelector('select')
|
|
220
|
+
.attributes.getNamedItem('data-v')) === null || _e === void 0 ? void 0 : _e.value;
|
|
221
|
+
const filter2Operator = (_f = formFilters[1]
|
|
222
|
+
.querySelector('.filterOperatorField')
|
|
223
|
+
.querySelector('select')
|
|
224
|
+
.attributes.getNamedItem('data-v')) === null || _f === void 0 ? void 0 : _f.value;
|
|
225
|
+
const filter2Value = formFilters[1]
|
|
226
|
+
.querySelector('.filterValueField')
|
|
227
|
+
.querySelector('ix-date').value;
|
|
228
|
+
expect(formFilters.length).to.be.eq(2);
|
|
229
|
+
expect(filter1Name).to.be.eq('firstName');
|
|
230
|
+
expect(filter1Operator).to.be.eq('contains');
|
|
231
|
+
expect(filter1Value).to.be.eq('John');
|
|
232
|
+
expect(filter2Name).to.be.eq('createdDate');
|
|
233
|
+
expect(filter2Operator).to.be.eq('equals');
|
|
234
|
+
expect(filter2Value).to.be.eq('2021-09-01');
|
|
235
|
+
});
|
|
236
|
+
it('should use maxDate for date inputs if provided', async () => {
|
|
237
|
+
var _a, _b;
|
|
238
|
+
const columnsWithDataType = [
|
|
239
|
+
{
|
|
240
|
+
name: 'dateField',
|
|
241
|
+
header: 'Date Field',
|
|
242
|
+
filterable: true,
|
|
243
|
+
filterOperators: ['='],
|
|
244
|
+
dataType: 'dateTime',
|
|
245
|
+
},
|
|
246
|
+
];
|
|
247
|
+
const el = await fixture(html `<ix-grid-row-filter
|
|
248
|
+
.columns=${columnsWithDataType}
|
|
249
|
+
.maxDate=${'2025-12-31'}
|
|
250
|
+
></ix-grid-row-filter>`);
|
|
251
|
+
const openMenu = (_a = el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('.filter-button');
|
|
252
|
+
openMenu === null || openMenu === void 0 ? void 0 : openMenu.click();
|
|
253
|
+
await elementUpdated(el);
|
|
254
|
+
const dateInput = (_b = el.shadowRoot) === null || _b === void 0 ? void 0 : _b.querySelector('div.filterValueField ix-date');
|
|
255
|
+
expect(dateInput).to.exist;
|
|
256
|
+
expect(dateInput === null || dateInput === void 0 ? void 0 : dateInput.getAttribute('max')).to.equal('2025-12-31');
|
|
257
|
+
});
|
|
258
|
+
it('should fallback to default maxDate if maxDate is not provided', async () => {
|
|
259
|
+
var _a, _b;
|
|
260
|
+
const columnsWithDataType = [
|
|
261
|
+
{
|
|
262
|
+
name: 'dateField',
|
|
263
|
+
header: 'Date Field',
|
|
264
|
+
filterable: true,
|
|
265
|
+
filterOperators: ['='],
|
|
266
|
+
dataType: 'dateTime',
|
|
267
|
+
},
|
|
268
|
+
];
|
|
269
|
+
const el = await fixture(html `<ix-grid-row-filter
|
|
270
|
+
.columns=${columnsWithDataType}
|
|
271
|
+
></ix-grid-row-filter>`);
|
|
272
|
+
const openMenu = (_a = el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('.filter-button');
|
|
273
|
+
openMenu === null || openMenu === void 0 ? void 0 : openMenu.click();
|
|
274
|
+
await elementUpdated(el);
|
|
275
|
+
const dateInput = (_b = el.shadowRoot) === null || _b === void 0 ? void 0 : _b.querySelector('div.filterValueField ix-date');
|
|
276
|
+
expect(dateInput).to.exist;
|
|
277
|
+
const today = new Date().toISOString().split('T')[0];
|
|
278
|
+
expect(dateInput === null || dateInput === void 0 ? void 0 : dateInput.getAttribute('max')).to.equal(today);
|
|
279
|
+
});
|
|
280
|
+
});
|
|
281
|
+
});
|
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
/* eslint-disable no-restricted-globals */
|
|
2
|
+
import { expect, fixture, oneEvent } from '@open-wc/testing';
|
|
3
|
+
import { html } from 'lit';
|
|
4
|
+
import { IxGridRowFilter } from '../components/IxGridRowFilter.js';
|
|
5
|
+
import '../ix-grid-no-rows.js';
|
|
6
|
+
import '../ix-grid.js';
|
|
7
|
+
const rows = [
|
|
8
|
+
{
|
|
9
|
+
name: 'one',
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
name: 'two',
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
name: 'three',
|
|
16
|
+
},
|
|
17
|
+
];
|
|
18
|
+
const columns = [
|
|
19
|
+
{
|
|
20
|
+
name: 'one',
|
|
21
|
+
header: 'one',
|
|
22
|
+
bodyRenderer: row => html ` <span>${row.name}</span>`,
|
|
23
|
+
filterable: true,
|
|
24
|
+
filterOperators: ['equals'],
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
name: 'two',
|
|
28
|
+
header: 'one',
|
|
29
|
+
bodyRenderer: row => html ` <span>${row.name}</span>`,
|
|
30
|
+
filterable: true,
|
|
31
|
+
filterOperators: ['equals', 'contains'],
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
name: 'three',
|
|
35
|
+
header: 'one',
|
|
36
|
+
bodyRenderer: row => html ` <span>${row.name}</span>`,
|
|
37
|
+
filterable: true,
|
|
38
|
+
filterOperators: ['equals', 'contains'],
|
|
39
|
+
},
|
|
40
|
+
];
|
|
41
|
+
describe('IxGrid', () => {
|
|
42
|
+
it('renders a grid', async () => {
|
|
43
|
+
const el = await fixture(html `<ix-grid></ix-grid>`);
|
|
44
|
+
expect(el).to.not.be.null;
|
|
45
|
+
});
|
|
46
|
+
it('renders the correct number of rows', async () => {
|
|
47
|
+
const el = await fixture(html `<ix-grid
|
|
48
|
+
.columns=${columns}
|
|
49
|
+
.rows=${rows}
|
|
50
|
+
></ix-grid>`);
|
|
51
|
+
expect(rows.length).to.equal(3);
|
|
52
|
+
expect(el).to.not.be.null;
|
|
53
|
+
});
|
|
54
|
+
it('renders no rows component state', async () => {
|
|
55
|
+
var _a;
|
|
56
|
+
const el = await fixture(html `<ix-grid
|
|
57
|
+
.columns=${columns}
|
|
58
|
+
.rows=${[]}
|
|
59
|
+
><ix-grid-no-rows slot="no-rows"></ix-grid-no-rows
|
|
60
|
+
></ix-grid>`);
|
|
61
|
+
const noRows = (_a = el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('slot[name="no-rows"]');
|
|
62
|
+
expect(noRows).to.not.be.null;
|
|
63
|
+
});
|
|
64
|
+
it('renders an ix-grid-row-filter', async () => {
|
|
65
|
+
var _a;
|
|
66
|
+
const el = await fixture(html `<ix-grid
|
|
67
|
+
.columns=${columns}
|
|
68
|
+
.rows=${rows}
|
|
69
|
+
.filterValueChangeDebounceTime=${1000}
|
|
70
|
+
></ix-grid>`);
|
|
71
|
+
const rowFilter = (_a = el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('ix-grid-row-filter');
|
|
72
|
+
expect(rowFilter).to.be.instanceOf(IxGridRowFilter);
|
|
73
|
+
expect(rowFilter.filterValueChangeDebounceTime).to.equal(1000);
|
|
74
|
+
});
|
|
75
|
+
it('resets pagination upon filter change', async () => {
|
|
76
|
+
var _a, _b;
|
|
77
|
+
const el = await fixture(html `<ix-grid
|
|
78
|
+
.columns=${columns}
|
|
79
|
+
.rows=${rows}
|
|
80
|
+
></ix-grid>`);
|
|
81
|
+
const pagination = (_a = el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('ix-pagination');
|
|
82
|
+
pagination.dispatchEvent(new CustomEvent('updatePagination', {
|
|
83
|
+
detail: {
|
|
84
|
+
page: 2,
|
|
85
|
+
pageSize: 10,
|
|
86
|
+
},
|
|
87
|
+
bubbles: true,
|
|
88
|
+
composed: true,
|
|
89
|
+
}));
|
|
90
|
+
expect(el.page).to.equal(2);
|
|
91
|
+
const rowFilter = (_b = el.shadowRoot) === null || _b === void 0 ? void 0 : _b.querySelector('ix-grid-row-filter');
|
|
92
|
+
rowFilter.dispatchEvent(new CustomEvent('rowFilter', {
|
|
93
|
+
detail: {
|
|
94
|
+
resetPage: true,
|
|
95
|
+
},
|
|
96
|
+
bubbles: true,
|
|
97
|
+
composed: true,
|
|
98
|
+
}));
|
|
99
|
+
expect(el.page).to.equal(1);
|
|
100
|
+
});
|
|
101
|
+
it('should read and set sort, order, page and page size from URL if readParamsFromURL is set to true', async () => {
|
|
102
|
+
const el = await fixture(html `<ix-grid
|
|
103
|
+
.columns=${columns}
|
|
104
|
+
.rows=${rows}
|
|
105
|
+
.readParamsFromURL=${true}
|
|
106
|
+
></ix-grid>`);
|
|
107
|
+
const url = '?sort=lastName&order=asc&page=2&size=5';
|
|
108
|
+
history.pushState(null, '', `${location.pathname}${url}`);
|
|
109
|
+
el.firstUpdated();
|
|
110
|
+
expect(el.page).to.be.eq(2);
|
|
111
|
+
expect(el.pageSize).to.be.eq(5);
|
|
112
|
+
expect(el.sortedColumn).to.be.eq('lastName');
|
|
113
|
+
expect(el.sortDirection).to.be.eq('asc');
|
|
114
|
+
});
|
|
115
|
+
it('should set sort, order, page, page size and filters in the URL when addParamsToURL is set to true', async () => {
|
|
116
|
+
var _a, _b;
|
|
117
|
+
const columnsWithFilters = [
|
|
118
|
+
{
|
|
119
|
+
name: 'firstName',
|
|
120
|
+
header: 'First name',
|
|
121
|
+
filterable: true,
|
|
122
|
+
dataType: 'string',
|
|
123
|
+
filterOperators: ['contains', 'equals'],
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
name: 'lastName',
|
|
127
|
+
header: 'Last Name',
|
|
128
|
+
filterable: true,
|
|
129
|
+
dataType: 'string',
|
|
130
|
+
filterOperators: ['equals'],
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
name: 'createdDate',
|
|
134
|
+
header: 'Created Date',
|
|
135
|
+
filterable: true,
|
|
136
|
+
dataType: 'dateTime',
|
|
137
|
+
filterOperators: ['equals'],
|
|
138
|
+
},
|
|
139
|
+
];
|
|
140
|
+
const existingSearchParams = '?realUsername=Earl&userAge=30';
|
|
141
|
+
history.pushState(null, '', `${location.pathname}${existingSearchParams}`);
|
|
142
|
+
const el = await fixture(html `<ix-grid
|
|
143
|
+
.columns=${columnsWithFilters}
|
|
144
|
+
.addParamsToURL=${true}
|
|
145
|
+
></ix-grid>`);
|
|
146
|
+
const rowFilter = (_a = el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('ix-grid-row-filter');
|
|
147
|
+
rowFilter.dispatchEvent(new CustomEvent('rowFilter', {
|
|
148
|
+
detail: {
|
|
149
|
+
filters: [
|
|
150
|
+
{
|
|
151
|
+
columnField: 'firstName',
|
|
152
|
+
operatorValue: 'contains',
|
|
153
|
+
value: 'test',
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
columnField: 'createdDate',
|
|
157
|
+
operatorValue: 'equals',
|
|
158
|
+
value: '2024-10-10',
|
|
159
|
+
},
|
|
160
|
+
],
|
|
161
|
+
},
|
|
162
|
+
bubbles: true,
|
|
163
|
+
composed: true,
|
|
164
|
+
}));
|
|
165
|
+
el.sortedColumn = 'firstName';
|
|
166
|
+
el.sortDirection = 'desc';
|
|
167
|
+
const pagination = (_b = el.shadowRoot) === null || _b === void 0 ? void 0 : _b.querySelector('ix-pagination');
|
|
168
|
+
pagination.dispatchEvent(new CustomEvent('updatePagination', {
|
|
169
|
+
detail: {
|
|
170
|
+
page: 3,
|
|
171
|
+
pageSize: 5,
|
|
172
|
+
},
|
|
173
|
+
bubbles: true,
|
|
174
|
+
composed: true,
|
|
175
|
+
}));
|
|
176
|
+
expect(location.search).to.be.eq(`${existingSearchParams}&sort=firstName&order=desc&page=3&size=5&firstName_contains=test&createdDate_equals=2024-10-10`);
|
|
177
|
+
});
|
|
178
|
+
describe('IxGrid updateSort', () => {
|
|
179
|
+
it('should update URL with sort, order, page, pageSize, filters', async () => {
|
|
180
|
+
history.replaceState(null, '', '/?userId=123&token=abc');
|
|
181
|
+
const updateSortColumns = [
|
|
182
|
+
{
|
|
183
|
+
name: 'name',
|
|
184
|
+
header: 'Name',
|
|
185
|
+
filterable: true,
|
|
186
|
+
filterOperators: ['contains'],
|
|
187
|
+
sortable: true,
|
|
188
|
+
},
|
|
189
|
+
];
|
|
190
|
+
const el = await fixture(html `
|
|
191
|
+
<ix-grid
|
|
192
|
+
.columns=${updateSortColumns}
|
|
193
|
+
.rows=${[]}
|
|
194
|
+
.addParamsToURL=${true}
|
|
195
|
+
.preservedQueryParamKeys=${['userId', 'token']}
|
|
196
|
+
></ix-grid>
|
|
197
|
+
`);
|
|
198
|
+
await el.updateComplete;
|
|
199
|
+
await el.handleSort('name');
|
|
200
|
+
const url = new URL(window.location.href);
|
|
201
|
+
const { searchParams } = url;
|
|
202
|
+
expect(searchParams.get('sort')).to.equal('name');
|
|
203
|
+
expect(searchParams.get('order')).to.equal('asc');
|
|
204
|
+
expect(searchParams.get('page')).to.equal('1');
|
|
205
|
+
expect(searchParams.get('size')).to.equal('10');
|
|
206
|
+
expect(searchParams.get('userId')).to.equal('123');
|
|
207
|
+
expect(searchParams.get('token')).to.equal('abc');
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
describe('IxGrid LocalStorage Persistence', () => {
|
|
211
|
+
beforeEach(() => {
|
|
212
|
+
localStorage.clear();
|
|
213
|
+
});
|
|
214
|
+
it('should reset localStorage value if columns length does not match', async () => {
|
|
215
|
+
const tempEl = await fixture(html `<ix-grid .columns=${columns} localStorageID="testGrid"></ix-grid>`);
|
|
216
|
+
const localStorageKey = tempEl.columnsLocalStorageKey;
|
|
217
|
+
// Simulating a user fully deleting and adding a fake column to their local storage
|
|
218
|
+
localStorage.setItem(localStorageKey, JSON.stringify([{ name: 'six', header: 'six' }]));
|
|
219
|
+
const el = await fixture(html `<ix-grid .columns=${columns} localStorageID="testGrid"></ix-grid>`);
|
|
220
|
+
await el.updateComplete;
|
|
221
|
+
const storedColumns = JSON.parse(localStorage.getItem(el.columnsLocalStorageKey) || '[]');
|
|
222
|
+
expect(storedColumns.length).to.equal(columns.length);
|
|
223
|
+
expect(storedColumns.map(c => c.name)).to.deep.equal(columns.map(c => c.name));
|
|
224
|
+
});
|
|
225
|
+
it('should reset localStorage value if it contains a column that does not exist in the provided columns', async () => {
|
|
226
|
+
const tempEl = await fixture(html `<ix-grid .columns=${columns} localStorageID="testGrid"></ix-grid>`);
|
|
227
|
+
const localStorageKey = tempEl.columnsLocalStorageKey;
|
|
228
|
+
// Simulating a user adding an additional column to local storage
|
|
229
|
+
localStorage.setItem(localStorageKey, JSON.stringify([
|
|
230
|
+
...columns,
|
|
231
|
+
{ name: 'nonexistent', header: 'Nonexistent' },
|
|
232
|
+
]));
|
|
233
|
+
const el = await fixture(html `<ix-grid .columns=${columns} localStorageID="testGrid"></ix-grid>`);
|
|
234
|
+
await el.updateComplete;
|
|
235
|
+
const storedColumns = JSON.parse(localStorage.getItem(el.columnsLocalStorageKey) || '[]');
|
|
236
|
+
expect(storedColumns.length).to.equal(columns.length);
|
|
237
|
+
expect(storedColumns.map(c => c.name)).to.deep.equal(columns.map(c => c.name));
|
|
238
|
+
});
|
|
239
|
+
it('should delete localStorage if a provided column does not exist in localStorage and not create new local storage', async () => {
|
|
240
|
+
const initialColumns = columns.slice(0, -1);
|
|
241
|
+
const tempEl = await fixture(html `<ix-grid
|
|
242
|
+
.columns=${initialColumns}
|
|
243
|
+
localStorageID="testGrid"
|
|
244
|
+
></ix-grid>`);
|
|
245
|
+
const localStorageKey = tempEl.columnsLocalStorageKey;
|
|
246
|
+
// Simulating a user deleting most columns from local storage
|
|
247
|
+
localStorage.setItem(localStorageKey, JSON.stringify(initialColumns));
|
|
248
|
+
const el = await fixture(html `<ix-grid .columns=${columns} localStorageID="testGrid"></ix-grid>`);
|
|
249
|
+
await el.updateComplete;
|
|
250
|
+
expect(localStorage.getItem(el.columnsLocalStorageKey)).to.equal(null);
|
|
251
|
+
});
|
|
252
|
+
it('should reorder columns based on table header flex order', async () => {
|
|
253
|
+
var _a;
|
|
254
|
+
const el = await fixture(html `<ix-grid
|
|
255
|
+
.columns=${columns}
|
|
256
|
+
.rows=${rows}
|
|
257
|
+
localStorageID="testGrid"
|
|
258
|
+
></ix-grid>`);
|
|
259
|
+
await el.updateComplete;
|
|
260
|
+
const headerCells = (_a = el.grid.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelectorAll('th');
|
|
261
|
+
if (!headerCells || headerCells.length !== columns.length) {
|
|
262
|
+
throw new Error('Table headers not found or do not match expected count');
|
|
263
|
+
}
|
|
264
|
+
const orderMap = [2, 0, 1];
|
|
265
|
+
headerCells.forEach((th, index) => {
|
|
266
|
+
const thElement = th;
|
|
267
|
+
thElement.style.order = orderMap[index].toString();
|
|
268
|
+
});
|
|
269
|
+
await el.updateComplete;
|
|
270
|
+
await el.reorderColumnsFromTable();
|
|
271
|
+
const reorderedColumns = [columns[1], columns[2], columns[0]];
|
|
272
|
+
const storedColumns = JSON.parse(localStorage.getItem(el.columnsLocalStorageKey) || '[]');
|
|
273
|
+
expect(storedColumns.map(c => c.name)).to.deep.equal(reorderedColumns.map(c => c.name));
|
|
274
|
+
});
|
|
275
|
+
it('should reorder columns when a filter reorder event is triggered', async () => {
|
|
276
|
+
const el = await fixture(html `<ix-grid .columns=${columns} localStorageID="testGrid"></ix-grid>`);
|
|
277
|
+
await el.updateComplete;
|
|
278
|
+
const reorderedColumns = [columns[2], columns[0], columns[1]];
|
|
279
|
+
const event = new CustomEvent('columnFilter', {
|
|
280
|
+
detail: { reorderedColumns },
|
|
281
|
+
bubbles: true,
|
|
282
|
+
composed: true,
|
|
283
|
+
});
|
|
284
|
+
el.reorderColumnsFromFilter(event);
|
|
285
|
+
await el.updateComplete;
|
|
286
|
+
const storedColumns = JSON.parse(localStorage.getItem(el.columnsLocalStorageKey) || '[]');
|
|
287
|
+
expect(el.displayColumns.map(c => c.name)).to.deep.equal(reorderedColumns.map(c => c.name));
|
|
288
|
+
expect(storedColumns.map(c => c.name)).to.deep.equal(reorderedColumns.map(c => c.name));
|
|
289
|
+
});
|
|
290
|
+
});
|
|
291
|
+
describe('IxGrid handlePopState', () => {
|
|
292
|
+
const handlePopStateColumns = [
|
|
293
|
+
{
|
|
294
|
+
name: 'customcolumnfilter',
|
|
295
|
+
header: 'Custom',
|
|
296
|
+
filterable: true,
|
|
297
|
+
filterOperators: ['contains'],
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
name: 'id',
|
|
301
|
+
header: 'ID',
|
|
302
|
+
filterable: true,
|
|
303
|
+
filterOperators: ['equals'],
|
|
304
|
+
},
|
|
305
|
+
];
|
|
306
|
+
it('should handle empty sort/order/page/size in URL', async () => {
|
|
307
|
+
history.replaceState(null, '', '/?sort=&order=&page=&size=');
|
|
308
|
+
const el = await fixture(html `
|
|
309
|
+
<ix-grid
|
|
310
|
+
.columns=${handlePopStateColumns}
|
|
311
|
+
.rows=${[]}
|
|
312
|
+
.readParamsFromURL=${true}
|
|
313
|
+
></ix-grid>
|
|
314
|
+
`);
|
|
315
|
+
await el.updateComplete;
|
|
316
|
+
const changeEventPromise = oneEvent(el, 'change');
|
|
317
|
+
window.dispatchEvent(new PopStateEvent('popstate'));
|
|
318
|
+
const event = await changeEventPromise;
|
|
319
|
+
expect(el.sortedColumn).to.equal('');
|
|
320
|
+
expect(el.sortDirection).to.equal('');
|
|
321
|
+
expect(el.page).to.equal(1);
|
|
322
|
+
expect(el.pageSize).to.equal(10);
|
|
323
|
+
expect(event.detail.filters).to.deep.equal({});
|
|
324
|
+
});
|
|
325
|
+
it('should handle valid sort/order/page/size in URL', async () => {
|
|
326
|
+
history.replaceState(null, '', '/?sort=id&order=desc&page=1&size=20');
|
|
327
|
+
const el = await fixture(html `
|
|
328
|
+
<ix-grid
|
|
329
|
+
.columns=${handlePopStateColumns}
|
|
330
|
+
.rows=${[]}
|
|
331
|
+
.readParamsFromURL=${true}
|
|
332
|
+
></ix-grid>
|
|
333
|
+
`);
|
|
334
|
+
await el.updateComplete;
|
|
335
|
+
const changeEventPromise = oneEvent(el, 'change');
|
|
336
|
+
window.dispatchEvent(new PopStateEvent('popstate'));
|
|
337
|
+
const event = await changeEventPromise;
|
|
338
|
+
expect(el.sortedColumn).to.equal('id');
|
|
339
|
+
expect(el.sortDirection).to.equal('desc');
|
|
340
|
+
expect(el.page).to.equal(1);
|
|
341
|
+
expect(el.pageSize).to.equal(20);
|
|
342
|
+
expect(event.detail.filters).to.deep.equal({});
|
|
343
|
+
});
|
|
344
|
+
it('should update filters from URL with custom filter', async () => {
|
|
345
|
+
history.replaceState(null, '', '/?customcolumnfilter_contains=foobar');
|
|
346
|
+
const el = await fixture(html `
|
|
347
|
+
<ix-grid
|
|
348
|
+
.columns=${handlePopStateColumns}
|
|
349
|
+
.rows=${[]}
|
|
350
|
+
.readParamsFromURL=${true}
|
|
351
|
+
></ix-grid>
|
|
352
|
+
`);
|
|
353
|
+
await el.updateComplete;
|
|
354
|
+
const changeEventPromise = oneEvent(el, 'change');
|
|
355
|
+
window.dispatchEvent(new PopStateEvent('popstate'));
|
|
356
|
+
const event = await changeEventPromise;
|
|
357
|
+
expect(event.detail.filters).to.deep.equal({
|
|
358
|
+
customcolumnfilter: 'foobar',
|
|
359
|
+
});
|
|
360
|
+
});
|
|
361
|
+
it('should dispatch a change event with correct detail', async () => {
|
|
362
|
+
history.replaceState(null, '', '/?sort=id&order=desc&page=1&size=20&customcolumnfilter_contains=foobar');
|
|
363
|
+
const el = await fixture(html `
|
|
364
|
+
<ix-grid
|
|
365
|
+
.columns=${handlePopStateColumns}
|
|
366
|
+
.rows=${[]}
|
|
367
|
+
.readParamsFromURL=${true}
|
|
368
|
+
></ix-grid>
|
|
369
|
+
`);
|
|
370
|
+
await el.updateComplete;
|
|
371
|
+
const changeEventPromise = oneEvent(el, 'change');
|
|
372
|
+
window.dispatchEvent(new PopStateEvent('popstate'));
|
|
373
|
+
const event = await changeEventPromise;
|
|
374
|
+
expect(event.detail).to.include({
|
|
375
|
+
columnName: 'id',
|
|
376
|
+
sortOrder: 'desc',
|
|
377
|
+
page: 1,
|
|
378
|
+
pageSize: 20,
|
|
379
|
+
});
|
|
380
|
+
expect(event.detail.filters).to.deep.equal({
|
|
381
|
+
customcolumnfilter: 'foobar',
|
|
382
|
+
});
|
|
383
|
+
expect(event.detail.filtersOperators).to.deep.equal([
|
|
384
|
+
{
|
|
385
|
+
columnField: 'customcolumnfilter',
|
|
386
|
+
operator: 'contains',
|
|
387
|
+
},
|
|
388
|
+
]);
|
|
389
|
+
});
|
|
390
|
+
});
|
|
391
|
+
});
|