@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.
@@ -1,4 +1,31 @@
1
- import { expect, waitFor } from 'storybook/test';
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)}"]`);
@@ -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;
@@ -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
- return {
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: colDef.sortable && params.defaultColDef?.sortable !== false,
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
- const colIdsSet = colIds instanceof Set ? colIds : new Set(colIds);
5646
- const getVisibleColStates = () => {
5647
- const colStates = gridApi.getColumnState();
5648
- return colStates.filter((colState) => {
5649
- const colId = colState.colId;
5650
- return (lodashEs.isEmpty(colIdsSet) || colIdsSet.has(colId)) && !userSizedColIds?.has(colId);
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
- const getFlexColStates = () => getVisibleColStates().filter(colStateFlexed);
5654
- const getNonFlexColStates = () => getVisibleColStates().filter(colStateNotFlexed);
5655
- // You cannot autosize flex columns it will break layout randomly
5656
- // So, a flex column is assumed to be 150 wide, unless width is provided
5657
- const flexColumns = getFlexColStates().map(colStateId);
5658
- let width = 0;
5659
- flexColumns.forEach((colId) => {
5660
- const colDef = gridApi.getColumnDef(colId);
5661
- width += colDef?.flexAutoSizeWidth ?? 200;
5662
- });
5663
- const nonFlexColumns = getNonFlexColStates();
5664
- const nonFlexColumnIds = nonFlexColumns.map(colStateId);
5665
- gridApi.autoSizeColumns({ colIds: nonFlexColumnIds, skipHeader });
5666
- const calcSubWidth = () => {
5667
- const updatedFlexColumns = getVisibleColStates().filter((colState) => nonFlexColumnIds.includes(colState.colId));
5668
- return lodashEs.sumBy(updatedFlexColumns.filter((col) => !col.hide), 'width');
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;