@linzjs/step-ag-grid 29.11.4 → 29.12.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/dist/src/utils/__tests__/storybookTestUtil.ts +28 -1
- package/dist/src/utils/__tests__/testQuick.ts +2 -0
- package/dist/src/utils/util.d.ts +1 -0
- package/dist/step-ag-grid.cjs +226 -41
- package/dist/step-ag-grid.cjs.map +1 -1
- package/dist/step-ag-grid.esm.js +227 -43
- package/dist/step-ag-grid.esm.js.map +1 -1
- package/package.json +2 -1
- package/src/components/Grid.tsx +48 -4
- package/src/contexts/GridContextProvider.tsx +50 -45
- package/src/stories/grid/GridSorting.stories.tsx +189 -0
- package/src/utils/__tests__/storybookTestUtil.ts +28 -1
- package/src/utils/__tests__/testQuick.ts +2 -0
- package/src/utils/util.ts +19 -0
|
@@ -1,4 +1,31 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { sortBy } from 'lodash-es';
|
|
2
|
+
import { expect, userEvent, waitFor } from 'storybook/test';
|
|
3
|
+
|
|
4
|
+
import { findQuick, getAllQuick } from './testQuick';
|
|
2
5
|
|
|
3
6
|
export const waitForGridReady = ({ canvasElement }: { canvasElement: HTMLElement }) =>
|
|
4
7
|
waitFor(() => expect(canvasElement.querySelector('.Grid-ready')).toBeInTheDocument(), { timeout: 5000 });
|
|
8
|
+
|
|
9
|
+
export const waitForGridRows = async (props?: { grid?: HTMLElement; timeout?: number }) =>
|
|
10
|
+
waitFor(() => expect(getAllQuick({ classes: '.ag-row' }, props?.grid).length > 0).toBe(true), {
|
|
11
|
+
timeout: props?.timeout ?? 4000,
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
export const clickColumnHeaderToSort = async (colId: string, within: HTMLElement): Promise<void> => {
|
|
15
|
+
const user = userEvent.setup({
|
|
16
|
+
delay: 100,
|
|
17
|
+
});
|
|
18
|
+
const header = await findQuick({ selector: `.ag-header-cell[col-id="${colId}"]` }, within);
|
|
19
|
+
await expect(header).toBeInTheDocument();
|
|
20
|
+
// For some reason clicks in storybook don't trigger ag-grid, but manual clicks do
|
|
21
|
+
await user.click(header);
|
|
22
|
+
await user.keyboard('{Enter}');
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const gridColumnValues = (colId: string, within: HTMLElement): string[] => {
|
|
26
|
+
return sortBy(
|
|
27
|
+
getAllQuick({ tagName: `[col-id="${colId}"]`, classes: `.ag-cell` }, within),
|
|
28
|
+
// @ts-expect-error css style type
|
|
29
|
+
(el) => el.parentElement?.attributeStyleMap.get('transform')?.[0]?.y.value,
|
|
30
|
+
).map((cell) => cell.innerText);
|
|
31
|
+
};
|
|
@@ -25,6 +25,7 @@ export interface IQueryQuick {
|
|
|
25
25
|
role?: string;
|
|
26
26
|
classes?: string;
|
|
27
27
|
child?: IQueryQuick;
|
|
28
|
+
selector?: string;
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
const escapeSelectorParam = (param: string): string => param.replace(/["\\]/g, '\\$&');
|
|
@@ -51,6 +52,7 @@ const quickSelector = <T extends HTMLElement>(
|
|
|
51
52
|
let lastIQueryQuick = props;
|
|
52
53
|
for (let loop: IQueryQuick | undefined = props; loop; loop = loop.child) {
|
|
53
54
|
lastIQueryQuick = loop;
|
|
55
|
+
loop.selector && (selector += loop.selector);
|
|
54
56
|
loop.tagName && (selector += loop.tagName);
|
|
55
57
|
loop.ariaLabel && (selector += `[aria-label='${escapeSelectorParam(loop.ariaLabel)}']`);
|
|
56
58
|
loop.role && (selector += `[role="${escapeSelectorParam(loop.role)}"]`);
|
package/dist/src/utils/util.d.ts
CHANGED
|
@@ -6,3 +6,4 @@ export declare const hasParentClass: (className: string, child: Node) => boolean
|
|
|
6
6
|
export declare const stringByteLengthIsInvalid: (str: string, maxBytes: number) => boolean;
|
|
7
7
|
export declare const fnOrVar: (fn: any, param?: any) => any;
|
|
8
8
|
export declare const sanitiseFileName: (filename: string) => string;
|
|
9
|
+
export declare const compareNaturalInsensitive: (a?: object | string | number | null, b?: object | string | number | null) => number;
|
package/dist/step-ag-grid.cjs
CHANGED
|
@@ -592,6 +592,131 @@ const GridUpdatingContext = React.createContext({
|
|
|
592
592
|
updatedDep: 0,
|
|
593
593
|
});
|
|
594
594
|
|
|
595
|
+
var lib = {};
|
|
596
|
+
|
|
597
|
+
var hasRequiredLib;
|
|
598
|
+
|
|
599
|
+
function requireLib () {
|
|
600
|
+
if (hasRequiredLib) return lib;
|
|
601
|
+
hasRequiredLib = 1;
|
|
602
|
+
Object.defineProperty(lib, "__esModule", { value: true });
|
|
603
|
+
function natsort(options) {
|
|
604
|
+
if (options === void 0) { options = {}; }
|
|
605
|
+
var ore = /^0/;
|
|
606
|
+
var sre = /\s+/g;
|
|
607
|
+
var tre = /^\s+|\s+$/g;
|
|
608
|
+
// unicode
|
|
609
|
+
var ure = /[^\x00-\x80]/;
|
|
610
|
+
// hex
|
|
611
|
+
var hre = /^0x[0-9a-f]+$/i;
|
|
612
|
+
// numeric
|
|
613
|
+
var nre = /(0x[\da-fA-F]+|(^[\+\-]?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?(?=\D|\s|$))|\d+)/g;
|
|
614
|
+
// datetime
|
|
615
|
+
var dre = /(^([\w ]+,?[\w ]+)?[\w ]+,?[\w ]+\d+:\d+(:\d+)?[\w ]?|^\d{1,4}[\/\-]\d{1,4}[\/\-]\d{1,4}|^\w+, \w+ \d+, \d{4})/; // tslint:disable-line
|
|
616
|
+
var toLowerCase = String.prototype.toLocaleLowerCase || String.prototype.toLowerCase;
|
|
617
|
+
var GREATER = options.desc ? -1 : 1;
|
|
618
|
+
var SMALLER = -GREATER;
|
|
619
|
+
var normalize = options.insensitive
|
|
620
|
+
? function (s) { return toLowerCase.call("" + s).replace(tre, ''); }
|
|
621
|
+
: function (s) { return ("" + s).replace(tre, ''); };
|
|
622
|
+
function tokenize(s) {
|
|
623
|
+
return s.replace(nre, '\0$1\0')
|
|
624
|
+
.replace(/\0$/, '')
|
|
625
|
+
.replace(/^\0/, '')
|
|
626
|
+
.split('\0');
|
|
627
|
+
}
|
|
628
|
+
function parse(s, l) {
|
|
629
|
+
// normalize spaces; find floats not starting with '0',
|
|
630
|
+
// string or 0 if not defined (Clint Priest)
|
|
631
|
+
return (!s.match(ore) || l === 1)
|
|
632
|
+
&& parseFloat(s)
|
|
633
|
+
|| s.replace(sre, ' ').replace(tre, '')
|
|
634
|
+
|| 0;
|
|
635
|
+
}
|
|
636
|
+
return function (a, b) {
|
|
637
|
+
// trim pre-post whitespace
|
|
638
|
+
var aa = normalize(a);
|
|
639
|
+
var bb = normalize(b);
|
|
640
|
+
// return immediately if at least one of the values is empty.
|
|
641
|
+
// empty string < any others
|
|
642
|
+
if (!aa && !bb) {
|
|
643
|
+
return 0;
|
|
644
|
+
}
|
|
645
|
+
if (!aa && bb) {
|
|
646
|
+
return SMALLER;
|
|
647
|
+
}
|
|
648
|
+
if (aa && !bb) {
|
|
649
|
+
return GREATER;
|
|
650
|
+
}
|
|
651
|
+
// tokenize: split numeric strings and default strings
|
|
652
|
+
var aArr = tokenize(aa);
|
|
653
|
+
var bArr = tokenize(bb);
|
|
654
|
+
// hex or date detection
|
|
655
|
+
var aHex = aa.match(hre);
|
|
656
|
+
var bHex = bb.match(hre);
|
|
657
|
+
var av = (aHex && bHex) ? parseInt(aHex[0], 16) : (aArr.length !== 1 && Date.parse(aa));
|
|
658
|
+
var bv = (aHex && bHex)
|
|
659
|
+
? parseInt(bHex[0], 16)
|
|
660
|
+
: av && bb.match(dre) && Date.parse(bb) || null;
|
|
661
|
+
// try and sort Hex codes or Dates
|
|
662
|
+
if (bv) {
|
|
663
|
+
if (av === bv) {
|
|
664
|
+
return 0;
|
|
665
|
+
}
|
|
666
|
+
if (av < bv) {
|
|
667
|
+
return SMALLER;
|
|
668
|
+
}
|
|
669
|
+
if (av > bv) {
|
|
670
|
+
return GREATER;
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
var al = aArr.length;
|
|
674
|
+
var bl = bArr.length;
|
|
675
|
+
// handle numeric strings and default strings
|
|
676
|
+
for (var i = 0, l = Math.max(al, bl); i < l; i += 1) {
|
|
677
|
+
var af = parse(aArr[i] || '', al);
|
|
678
|
+
var bf = parse(bArr[i] || '', bl);
|
|
679
|
+
// handle numeric vs string comparison.
|
|
680
|
+
// numeric < string
|
|
681
|
+
if (isNaN(af) !== isNaN(bf)) {
|
|
682
|
+
return isNaN(af) ? GREATER : SMALLER;
|
|
683
|
+
}
|
|
684
|
+
// if unicode use locale comparison
|
|
685
|
+
if (ure.test(af + bf) && af.localeCompare) {
|
|
686
|
+
var comp = af.localeCompare(bf);
|
|
687
|
+
if (comp > 0) {
|
|
688
|
+
return GREATER;
|
|
689
|
+
}
|
|
690
|
+
if (comp < 0) {
|
|
691
|
+
return SMALLER;
|
|
692
|
+
}
|
|
693
|
+
if (i === l - 1) {
|
|
694
|
+
return 0;
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
if (af < bf) {
|
|
698
|
+
return SMALLER;
|
|
699
|
+
}
|
|
700
|
+
if (af > bf) {
|
|
701
|
+
return GREATER;
|
|
702
|
+
}
|
|
703
|
+
if ("" + af < "" + bf) {
|
|
704
|
+
return SMALLER;
|
|
705
|
+
}
|
|
706
|
+
if ("" + af > "" + bf) {
|
|
707
|
+
return GREATER;
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
return 0;
|
|
711
|
+
};
|
|
712
|
+
}
|
|
713
|
+
lib.default = natsort;
|
|
714
|
+
return lib;
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
var libExports = requireLib();
|
|
718
|
+
var natsort = /*@__PURE__*/getDefaultExportFromCjs(libExports);
|
|
719
|
+
|
|
595
720
|
const isNotEmpty = lodashEs.negate(lodashEs.isEmpty);
|
|
596
721
|
const wait = (timeoutMs) => new Promise((resolve) => {
|
|
597
722
|
setTimeout(resolve, timeoutMs);
|
|
@@ -643,6 +768,20 @@ const sanitiseFileName = (filename) => {
|
|
|
643
768
|
return valid.slice().slice(0, 64);
|
|
644
769
|
return valid.slice(0, -fileExt.length - 1).slice(0, 64) + '.' + fileExt;
|
|
645
770
|
};
|
|
771
|
+
const naturalSortInsensitive = natsort({ insensitive: true });
|
|
772
|
+
const santitizeNaturalValue = (v) => {
|
|
773
|
+
if (v === '–' || v === '-' || v == null) {
|
|
774
|
+
return '';
|
|
775
|
+
}
|
|
776
|
+
return String(v);
|
|
777
|
+
};
|
|
778
|
+
const compareNaturalInsensitive = (a, b) => {
|
|
779
|
+
if ((a !== null && typeof a === 'object') || (b !== null && typeof b === 'object')) {
|
|
780
|
+
// We can't compare objects
|
|
781
|
+
return 0;
|
|
782
|
+
}
|
|
783
|
+
return naturalSortInsensitive(santitizeNaturalValue(a), santitizeNaturalValue(b));
|
|
784
|
+
};
|
|
646
785
|
|
|
647
786
|
/**
|
|
648
787
|
* AgGrid checkbox select does not pass clicks within cell but not on the checkbox to checkbox.
|
|
@@ -3126,18 +3265,48 @@ const Grid = ({ 'data-testid': dataTestId, defaultPostSort = true, rowSelection
|
|
|
3126
3265
|
});
|
|
3127
3266
|
const adjustColDef = (colDef) => {
|
|
3128
3267
|
const flex = colDef.flex ?? (sizeColumns === 'fit' ? 1 : undefined);
|
|
3129
|
-
|
|
3268
|
+
const valueFormatter = colDef.valueFormatter;
|
|
3269
|
+
const sortable = colDef.sortable && params.defaultColDef?.sortable !== false;
|
|
3270
|
+
let comparator = colDef.comparator;
|
|
3271
|
+
if (sortable && !comparator) {
|
|
3272
|
+
comparator = (value1, value2, node1, node2) => {
|
|
3273
|
+
let r = 0;
|
|
3274
|
+
if (typeof valueFormatter === 'function') {
|
|
3275
|
+
r = compareNaturalInsensitive(valueFormatter({
|
|
3276
|
+
data: node1.data,
|
|
3277
|
+
value: value1,
|
|
3278
|
+
node: node1,
|
|
3279
|
+
colDef: adjustedColDef,
|
|
3280
|
+
...NotAGridValueFormatterCall,
|
|
3281
|
+
}), valueFormatter({
|
|
3282
|
+
data: node2.data,
|
|
3283
|
+
value: value2,
|
|
3284
|
+
node: node2,
|
|
3285
|
+
colDef: adjustedColDef,
|
|
3286
|
+
...NotAGridValueFormatterCall,
|
|
3287
|
+
}));
|
|
3288
|
+
}
|
|
3289
|
+
else {
|
|
3290
|
+
r = compareNaturalInsensitive(value1, value2);
|
|
3291
|
+
}
|
|
3292
|
+
// secondary compare as primary sort column rows are equal
|
|
3293
|
+
return r === 0 ? compareNaturalInsensitive(node1.data?.id, node2.data?.id) : r;
|
|
3294
|
+
};
|
|
3295
|
+
}
|
|
3296
|
+
const adjustedColDef = {
|
|
3130
3297
|
...colDef,
|
|
3131
3298
|
// You cannot pass a width to a flex
|
|
3132
3299
|
width: !!colDef.flex ? undefined : colDef.width,
|
|
3133
|
-
flexAutoSizeWidth: colDef.width,
|
|
3300
|
+
...(!!colDef.flex && { flexAutoSizeWidth: colDef.width }),
|
|
3134
3301
|
// If this is allowed flex columns don't size based on flex
|
|
3135
3302
|
suppressSizeToFit: true,
|
|
3136
3303
|
// Auto-sizing flex columns breaks everything
|
|
3137
3304
|
flex,
|
|
3138
3305
|
suppressAutoSize: !!flex,
|
|
3139
|
-
sortable
|
|
3306
|
+
sortable,
|
|
3307
|
+
comparator,
|
|
3140
3308
|
};
|
|
3309
|
+
return adjustedColDef;
|
|
3141
3310
|
};
|
|
3142
3311
|
return columnDefs.map((colDef) => adjustColDefOrGroup(colDef));
|
|
3143
3312
|
}, [columnDefs, params.defaultColDef?.sortable, sizeColumns]);
|
|
@@ -3380,6 +3549,15 @@ const quickFilterParser = (filterStr) => {
|
|
|
3380
3549
|
// filter is exact matches exactly groups separated by commas
|
|
3381
3550
|
return filterStr.split(',').map((str) => str.trim());
|
|
3382
3551
|
};
|
|
3552
|
+
/**
|
|
3553
|
+
* Columns to indicate to user when they debug why things are broken in the default comparator if their valueFormatter
|
|
3554
|
+
* is too complicated.
|
|
3555
|
+
*/
|
|
3556
|
+
const NotAGridValueFormatterCall = {
|
|
3557
|
+
column: 'Default comparator has no access to column, write your own comparator',
|
|
3558
|
+
api: 'Default comparator has no access to api, write your own comparator',
|
|
3559
|
+
context: 'Default comparator has no access to context, write your own comparator',
|
|
3560
|
+
};
|
|
3383
3561
|
|
|
3384
3562
|
const GridPopoverContext = React.createContext({
|
|
3385
3563
|
anchorRef: { current: null },
|
|
@@ -5642,47 +5820,53 @@ const GridContextProvider = (props) => {
|
|
|
5642
5820
|
if (!gridApi || !gridApi.getColumnState()) {
|
|
5643
5821
|
return null;
|
|
5644
5822
|
}
|
|
5645
|
-
|
|
5646
|
-
|
|
5647
|
-
const
|
|
5648
|
-
|
|
5649
|
-
|
|
5650
|
-
|
|
5823
|
+
try {
|
|
5824
|
+
const colIdsSet = colIds instanceof Set ? colIds : new Set(colIds);
|
|
5825
|
+
const getVisibleColStates = () => {
|
|
5826
|
+
const colStates = gridApi.getColumnState();
|
|
5827
|
+
return colStates.filter((colState) => {
|
|
5828
|
+
const colId = colState.colId;
|
|
5829
|
+
return (lodashEs.isEmpty(colIdsSet) || colIdsSet.has(colId)) && !userSizedColIds?.has(colId);
|
|
5830
|
+
});
|
|
5831
|
+
};
|
|
5832
|
+
const getFlexColStates = () => getVisibleColStates().filter(colStateFlexed);
|
|
5833
|
+
const getNonFlexColStates = () => getVisibleColStates().filter(colStateNotFlexed);
|
|
5834
|
+
// You cannot autosize flex columns it will break layout randomly
|
|
5835
|
+
// So, a flex column is assumed to be 150 wide, unless width is provided
|
|
5836
|
+
const flexColumns = getFlexColStates().map(colStateId);
|
|
5837
|
+
let width = 0;
|
|
5838
|
+
flexColumns.forEach((colId) => {
|
|
5839
|
+
const colDef = gridApi.getColumnDef(colId);
|
|
5840
|
+
width += colDef?.flexAutoSizeWidth ?? 200;
|
|
5651
5841
|
});
|
|
5652
|
-
|
|
5653
|
-
|
|
5654
|
-
|
|
5655
|
-
|
|
5656
|
-
|
|
5657
|
-
|
|
5658
|
-
|
|
5659
|
-
|
|
5660
|
-
|
|
5661
|
-
|
|
5662
|
-
|
|
5663
|
-
|
|
5664
|
-
|
|
5665
|
-
|
|
5666
|
-
|
|
5667
|
-
|
|
5668
|
-
|
|
5669
|
-
};
|
|
5670
|
-
// ag-grid updates widths asynchronously, so we wait here for the default calculated width to change
|
|
5671
|
-
let lastSubWidth = calcSubWidth();
|
|
5672
|
-
const endTime = Date.now() + 1000;
|
|
5673
|
-
while (Date.now() < endTime) {
|
|
5674
|
-
await wait(40);
|
|
5675
|
-
const newSubWidth = calcSubWidth();
|
|
5676
|
-
if (lastSubWidth !== newSubWidth) {
|
|
5677
|
-
lastSubWidth = newSubWidth;
|
|
5678
|
-
break;
|
|
5842
|
+
const nonFlexColumns = getNonFlexColStates();
|
|
5843
|
+
const nonFlexColumnIds = nonFlexColumns.map(colStateId);
|
|
5844
|
+
gridApi.autoSizeColumns({ colIds: nonFlexColumnIds, skipHeader });
|
|
5845
|
+
const calcSubWidth = () => {
|
|
5846
|
+
const updatedFlexColumns = getVisibleColStates().filter((colState) => nonFlexColumnIds.includes(colState.colId));
|
|
5847
|
+
return lodashEs.sumBy(updatedFlexColumns.filter((col) => !col.hide), 'width');
|
|
5848
|
+
};
|
|
5849
|
+
// ag-grid updates widths asynchronously, so we wait here for the default calculated width to change
|
|
5850
|
+
let lastSubWidth = calcSubWidth();
|
|
5851
|
+
const endTime = Date.now() + 1000;
|
|
5852
|
+
while (Date.now() < endTime) {
|
|
5853
|
+
await wait(40);
|
|
5854
|
+
const newSubWidth = calcSubWidth();
|
|
5855
|
+
if (lastSubWidth !== newSubWidth) {
|
|
5856
|
+
lastSubWidth = newSubWidth;
|
|
5857
|
+
break;
|
|
5858
|
+
}
|
|
5679
5859
|
}
|
|
5860
|
+
width += lastSubWidth;
|
|
5861
|
+
gridApi.sizeColumnsToFit();
|
|
5862
|
+
return {
|
|
5863
|
+
width,
|
|
5864
|
+
};
|
|
5865
|
+
}
|
|
5866
|
+
catch (ex) {
|
|
5867
|
+
console.info('autosize failed', ex);
|
|
5868
|
+
return null;
|
|
5680
5869
|
}
|
|
5681
|
-
width += lastSubWidth;
|
|
5682
|
-
gridApi.sizeColumnsToFit();
|
|
5683
|
-
return {
|
|
5684
|
-
width,
|
|
5685
|
-
};
|
|
5686
5870
|
}, [gridApi]);
|
|
5687
5871
|
/**
|
|
5688
5872
|
* Resize columns to fit container
|
|
@@ -6249,6 +6433,7 @@ exports.bearingValueFormatter = bearingValueFormatter;
|
|
|
6249
6433
|
exports.colStateFlexed = colStateFlexed;
|
|
6250
6434
|
exports.colStateId = colStateId;
|
|
6251
6435
|
exports.colStateNotFlexed = colStateNotFlexed;
|
|
6436
|
+
exports.compareNaturalInsensitive = compareNaturalInsensitive;
|
|
6252
6437
|
exports.convertDDToDMS = convertDDToDMS;
|
|
6253
6438
|
exports.createCheckboxMultiFilterParams = createCheckboxMultiFilterParams;
|
|
6254
6439
|
exports.defaultValueFormatter = defaultValueFormatter;
|