diff --git a/packages/common/src/global-grid-options.ts b/packages/common/src/global-grid-options.ts index 02c006eb2..98ef472b4 100644 --- a/packages/common/src/global-grid-options.ts +++ b/packages/common/src/global-grid-options.ts @@ -182,7 +182,7 @@ export const GlobalGridOptions: GridOption = { iconClearFilterCommand: 'fa fa-filter mdi mdi mdi-filter-remove-outline', iconClearSortCommand: 'fa fa-unsorted mdi mdi-swap-vertical', iconFreezeColumns: 'fa fa-thumb-tack mdi mdi-pin-outline', - iconSortAscCommand: 'fa fa-sort-amount-asc mdi mdi-sort-ascending', + iconSortAscCommand: 'fa fa-sort-amount-asc mdi mdi-flip-v mdi-sort-ascending', iconSortDescCommand: 'fa fa-sort-amount-desc mdi mdi-flip-v mdi-sort-descending', iconColumnHideCommand: 'fa fa-times mdi mdi-close', hideColumnHideCommand: false, diff --git a/packages/common/src/interfaces/column.interface.ts b/packages/common/src/interfaces/column.interface.ts index a872b051a..c76c59cbf 100644 --- a/packages/common/src/interfaces/column.interface.ts +++ b/packages/common/src/interfaces/column.interface.ts @@ -261,7 +261,8 @@ export interface Column { validator?: EditorValidator; /** - * Can the value be undefined? Typically undefined values are disregarded when sorting, when set this flag will adds extra logic to Sorting and also sort undefined value. + * Defaults to false, can the value be undefined? + * Typically undefined values are disregarded when sorting, when setting this flag it will adds extra logic to Sorting and also sort undefined value. * This is an extra flag that user has to enable by themselve because Sorting undefined values has unwanted behavior in some use case * (for example Row Detail has UI inconsistencies since undefined is used in the plugin's logic) */ diff --git a/packages/common/src/interfaces/gridOption.interface.ts b/packages/common/src/interfaces/gridOption.interface.ts index 691123aac..d0488b784 100644 --- a/packages/common/src/interfaces/gridOption.interface.ts +++ b/packages/common/src/interfaces/gridOption.interface.ts @@ -81,6 +81,14 @@ export interface GridOption { /** Cell menu options (Action menu) */ cellMenu?: CellMenu; + /** + * Defaults to false, can the cell value (dataContext) be undefined? + * Typically undefined values are disregarded when sorting, when setting this flag it will adds extra logic to Sorting and also sort undefined value. + * This is an extra flag that user has to enable by themselve because Sorting undefined values has unwanted behavior in some use case + * (for example Row Detail has UI inconsistencies since undefined is used in the plugin's logic) + */ + cellValueCouldBeUndefined?: boolean; + /** Checkbox Select Plugin options (columnId, cssClass, toolTip, width) */ checkboxSelector?: CheckboxSelectorOption; diff --git a/packages/common/src/interfaces/sorter.interface.ts b/packages/common/src/interfaces/sorter.interface.ts index 6ed338826..1523e7a8d 100644 --- a/packages/common/src/interfaces/sorter.interface.ts +++ b/packages/common/src/interfaces/sorter.interface.ts @@ -1,4 +1,5 @@ import { Column } from './column.interface'; +import { GridOption } from './gridOption.interface'; import { SortDirectionNumber } from '../enums/sortDirectionNumber.enum'; -export type SortComparer = (value1: any, value2: any, sortDirection: SortDirectionNumber, sortColumn?: Column) => number; +export type SortComparer = (value1: any, value2: any, sortDirection: SortDirectionNumber, sortColumn?: Column, gridOptions?: GridOption) => number; diff --git a/packages/common/src/services/__tests__/collection.service.spec.ts b/packages/common/src/services/__tests__/collection.service.spec.ts index e50a3ea95..0d024c50f 100644 --- a/packages/common/src/services/__tests__/collection.service.spec.ts +++ b/packages/common/src/services/__tests__/collection.service.spec.ts @@ -288,7 +288,7 @@ describe('CollectionService', () => { describe('sortCollection method', () => { it('should return a collection of numbers sorted', () => { translateService.use('en'); - const columnDef = { id: 'count', field: 'count', fieldType: FieldType.number } as Column; + const columnDef = { id: 'count', field: 'count', type: FieldType.number } as Column; const result1 = service.sortCollection(columnDef, [0, -11, 3, 99999, -200], { sortDesc: false } as CollectionSortBy); const result2 = service.sortCollection(columnDef, [0, -11, 3, 99999, -200], { sortDesc: true } as CollectionSortBy); @@ -300,7 +300,7 @@ describe('CollectionService', () => { it('should return a collection of translation values sorted', () => { translateService.use('en'); const roleCollection = ['SALES_REP', 'DEVELOPER', 'SALES_REP', null, 'HUMAN_RESOURCES', 'FINANCE_MANAGER', 'UNKNOWN']; - const columnDef = { id: 'count', field: 'count', fieldType: FieldType.string } as Column; + const columnDef = { id: 'count', field: 'count', type: FieldType.string } as Column; const result1 = service.sortCollection(columnDef, [...roleCollection], { sortDesc: false } as CollectionSortBy, true); const result2 = service.sortCollection(columnDef, [...roleCollection], { sortDesc: true } as CollectionSortBy, true); diff --git a/packages/common/src/services/collection.service.ts b/packages/common/src/services/collection.service.ts index fdd9811a2..3862d227b 100644 --- a/packages/common/src/services/collection.service.ts +++ b/packages/common/src/services/collection.service.ts @@ -110,13 +110,13 @@ export class CollectionService { for (let i = 0, l = sortByOptions.length; i < l; i++) { const sortBy = sortByOptions[i]; - if (sortBy && sortBy.property) { + if (sortBy?.property) { // collection of objects with a property name provided const sortDirection = sortBy.sortDesc ? SortDirectionNumber.desc : SortDirectionNumber.asc; const objectProperty = sortBy.property; - const fieldType = sortBy.fieldType || FieldType.string; - const value1 = (enableTranslateLabel) ? this.translaterService && this.translaterService.translate && this.translaterService.getCurrentLanguage && this.translaterService.getCurrentLanguage() && this.translaterService.translate(dataRow1[objectProperty] || ' ') : dataRow1[objectProperty]; - const value2 = (enableTranslateLabel) ? this.translaterService && this.translaterService.translate && this.translaterService.getCurrentLanguage && this.translaterService.getCurrentLanguage() && this.translaterService.translate(dataRow2[objectProperty] || ' ') : dataRow2[objectProperty]; + const fieldType = sortBy?.fieldType ?? columnDef?.type ?? FieldType.string; + const value1 = (enableTranslateLabel) ? this.translaterService?.translate && this.translaterService.translate(dataRow1[objectProperty] || ' ') : dataRow1[objectProperty]; + const value2 = (enableTranslateLabel) ? this.translaterService?.translate && this.translaterService.translate(dataRow2[objectProperty] || ' ') : dataRow2[objectProperty]; const sortResult = sortByFieldType(fieldType, value1, value2, sortDirection, columnDef); if (sortResult !== SortDirectionNumber.neutral) { @@ -126,16 +126,16 @@ export class CollectionService { } return SortDirectionNumber.neutral; }); - } else if (sortByOptions && sortByOptions.property) { + } else if (sortByOptions?.property) { // single sort // collection of objects with a property name provided const objectProperty = sortByOptions.property; const sortDirection = sortByOptions.sortDesc ? SortDirectionNumber.desc : SortDirectionNumber.asc; - const fieldType = sortByOptions.fieldType || FieldType.string; + const fieldType = sortByOptions?.fieldType ?? columnDef?.type ?? FieldType.string; sortedCollection = collection.sort((dataRow1: T, dataRow2: T) => { - const value1 = (enableTranslateLabel) ? this.translaterService && this.translaterService.translate && this.translaterService.getCurrentLanguage && this.translaterService.getCurrentLanguage() && this.translaterService.translate(dataRow1[objectProperty] || ' ') : dataRow1[objectProperty]; - const value2 = (enableTranslateLabel) ? this.translaterService && this.translaterService.translate && this.translaterService.getCurrentLanguage && this.translaterService.getCurrentLanguage() && this.translaterService.translate(dataRow2[objectProperty] || ' ') : dataRow2[objectProperty]; + const value1 = (enableTranslateLabel) ? this.translaterService?.translate && this.translaterService.translate(dataRow1[objectProperty] || ' ') : dataRow1[objectProperty]; + const value2 = (enableTranslateLabel) ? this.translaterService?.translate && this.translaterService.translate(dataRow2[objectProperty] || ' ') : dataRow2[objectProperty]; const sortResult = sortByFieldType(fieldType, value1, value2, sortDirection, columnDef); if (sortResult !== SortDirectionNumber.neutral) { return sortResult; @@ -144,11 +144,11 @@ export class CollectionService { }); } else if (sortByOptions && !sortByOptions.property) { const sortDirection = sortByOptions.sortDesc ? SortDirectionNumber.desc : SortDirectionNumber.asc; - const fieldType = sortByOptions.fieldType || FieldType.string; + const fieldType = sortByOptions?.fieldType ?? columnDef?.type ?? FieldType.string; sortedCollection = collection.sort((dataRow1: any, dataRow2: any) => { - const value1 = (enableTranslateLabel) ? this.translaterService?.translate && this.translaterService.getCurrentLanguage && this.translaterService.getCurrentLanguage() && this.translaterService.translate(dataRow1 || ' ') : dataRow1; - const value2 = (enableTranslateLabel) ? this.translaterService?.translate && this.translaterService.getCurrentLanguage && this.translaterService.getCurrentLanguage() && this.translaterService.translate(dataRow2 || ' ') : dataRow2; + const value1 = (enableTranslateLabel) ? this.translaterService?.translate && this.translaterService.translate(dataRow1 || ' ') : dataRow1; + const value2 = (enableTranslateLabel) ? this.translaterService?.translate && this.translaterService.translate(dataRow2 || ' ') : dataRow2; const sortResult = sortByFieldType(fieldType, value1, value2, sortDirection, columnDef); if (sortResult !== SortDirectionNumber.neutral) { return sortResult; diff --git a/packages/common/src/services/sort.service.ts b/packages/common/src/services/sort.service.ts index 93af12482..d0a645366 100644 --- a/packages/common/src/services/sort.service.ts +++ b/packages/common/src/services/sort.service.ts @@ -416,7 +416,7 @@ export class SortService { } sortComparer(sortColumn: ColumnSort, dataRow1: any, dataRow2: any, querySortField?: string): number | undefined { - if (sortColumn && sortColumn.sortCol) { + if (sortColumn?.sortCol) { const columnDef = sortColumn.sortCol; const sortDirection = sortColumn.sortAsc ? SortDirectionNumber.asc : SortDirectionNumber.desc; let queryFieldName1 = querySortField || columnDef.queryFieldSorter || columnDef.queryField || columnDef.field; @@ -442,12 +442,12 @@ export class SortService { // user could provide his own custom Sorter if (columnDef.sortComparer) { - const customSortResult = columnDef.sortComparer(value1, value2, sortDirection, columnDef); + const customSortResult = columnDef.sortComparer(value1, value2, sortDirection, columnDef, this._gridOptions); if (customSortResult !== SortDirectionNumber.neutral) { return customSortResult; } } else { - const sortResult = sortByFieldType(fieldType, value1, value2, sortDirection, columnDef); + const sortResult = sortByFieldType(fieldType, value1, value2, sortDirection, columnDef, this._gridOptions); if (sortResult !== SortDirectionNumber.neutral) { return sortResult; } diff --git a/packages/common/src/sortComparers/__tests__/sortUtilities.spec.ts b/packages/common/src/sortComparers/__tests__/sortUtilities.spec.ts index 886bde71a..11e854bae 100644 --- a/packages/common/src/sortComparers/__tests__/sortUtilities.spec.ts +++ b/packages/common/src/sortComparers/__tests__/sortUtilities.spec.ts @@ -7,19 +7,19 @@ describe('sortUtilities', () => { it('should call the SortComparers.numeric when FieldType is number', () => { const spy = jest.spyOn(SortComparers, 'numeric'); sortByFieldType(FieldType.number, 0, 4, SortDirectionNumber.asc, { id: 'field1', field: 'field1' }); - expect(spy).toHaveBeenCalledWith(0, 4, SortDirectionNumber.asc, { id: 'field1', field: 'field1' }); + expect(spy).toHaveBeenCalledWith(0, 4, SortDirectionNumber.asc, { id: 'field1', field: 'field1' }, undefined); }); it('should call the SortComparers.numeric when FieldType is integer', () => { const spy = jest.spyOn(SortComparers, 'numeric'); sortByFieldType(FieldType.integer, 0, 4, SortDirectionNumber.asc, { id: 'field1', field: 'field1' }); - expect(spy).toHaveBeenCalledWith(0, 4, SortDirectionNumber.asc, { id: 'field1', field: 'field1' }); + expect(spy).toHaveBeenCalledWith(0, 4, SortDirectionNumber.asc, { id: 'field1', field: 'field1' }, undefined); }); it('should call the SortComparers.numeric when FieldType is float', () => { const spy = jest.spyOn(SortComparers, 'numeric'); sortByFieldType(FieldType.float, 0, 4, SortDirectionNumber.asc, { id: 'field1', field: 'field1' }); - expect(spy).toHaveBeenCalledWith(0, 4, SortDirectionNumber.asc, { id: 'field1', field: 'field1' }); + expect(spy).toHaveBeenCalledWith(0, 4, SortDirectionNumber.asc, { id: 'field1', field: 'field1' }, undefined); }); it('should call the SortComparers.string when FieldType is a string (which is also the default)', () => { @@ -27,7 +27,7 @@ describe('sortUtilities', () => { const string2 = 'Jane'; const spy = jest.spyOn(SortComparers, 'string'); sortByFieldType(FieldType.string, string1, string2, SortDirectionNumber.asc, { id: 'field1', field: 'field1' }); - expect(spy).toHaveBeenCalledWith(string1, string2, SortDirectionNumber.asc, { id: 'field1', field: 'field1' }); + expect(spy).toHaveBeenCalledWith(string1, string2, SortDirectionNumber.asc, { id: 'field1', field: 'field1' }, undefined); }); it('should call the SortComparers.objectString when FieldType is objectString', () => { @@ -36,6 +36,6 @@ describe('sortUtilities', () => { const mockColumn = { id: 'field1', field: 'field1', dataKey: 'firstName' } as Column; const spy = jest.spyOn(SortComparers, 'objectString'); sortByFieldType(FieldType.object, object1, object2, SortDirectionNumber.asc, mockColumn); - expect(spy).toHaveBeenCalledWith(object1, object2, SortDirectionNumber.asc, mockColumn); + expect(spy).toHaveBeenCalledWith(object1, object2, SortDirectionNumber.asc, mockColumn, undefined); }); }); diff --git a/packages/common/src/sortComparers/__tests__/stringSortComparer.spec.ts b/packages/common/src/sortComparers/__tests__/stringSortComparer.spec.ts index ce6349a6d..c19d79ca1 100644 --- a/packages/common/src/sortComparers/__tests__/stringSortComparer.spec.ts +++ b/packages/common/src/sortComparers/__tests__/stringSortComparer.spec.ts @@ -1,6 +1,6 @@ import { SortDirectionNumber } from '../../enums/index'; import { stringSortComparer } from '../stringSortComparer'; -import { Column } from '../../interfaces/column.interface'; +import { Column, GridOption } from '../../interfaces/index'; describe('the String SortComparer', () => { it('should return original unsorted array when no direction is provided', () => { @@ -45,7 +45,7 @@ describe('the String SortComparer', () => { expect(inputArray).toEqual(['zebra', 'amazon', 'abc', 'John', 'Abe', '@at', '', null, null]); }); - it('should return a sorted ascending array and move the undefined values to the end of the array when "valueCouldBeUndefined" is set', () => { + it('should return a sorted ascending array and move the undefined values to the end of the array when "valueCouldBeUndefined" is set in the column definition', () => { // from MDN specification quote: All undefined elements are sorted to the end of the array. const columnDef = { id: 'name', field: 'name', valueCouldBeUndefined: true } as Column; const direction = SortDirectionNumber.asc; @@ -54,7 +54,7 @@ describe('the String SortComparer', () => { expect(inputArray).toEqual(['', '@at', 'Abe', 'John', 'abc', 'amazon', 'zebra', undefined, undefined]); }); - it('should return a sorted descending array and move the undefined values to the end of the array when "valueCouldBeUndefined" is set', () => { + it('should return a sorted descending array and move the undefined values to the end of the array when "valueCouldBeUndefined" is set in the column definition', () => { // from MDN specification quote: All undefined elements are sorted to the end of the array. const columnDef = { id: 'name', field: 'name', valueCouldBeUndefined: true } as Column; const direction = SortDirectionNumber.desc; @@ -62,4 +62,22 @@ describe('the String SortComparer', () => { inputArray.sort((value1, value2) => stringSortComparer(value1, value2, direction, columnDef)); expect(inputArray).toEqual(['zebra', 'amazon', 'abc', 'John', 'Abe', '@at', '', undefined, undefined]); }); + + it('should return a sorted ascending array and move the undefined values to the end of the array when "cellValueCouldBeUndefined" is set in the grid options', () => { + // from MDN specification quote: All undefined elements are sorted to the end of the array. + const columnDef = { id: 'name', field: 'name' } as Column; + const direction = SortDirectionNumber.asc; + const inputArray = ['amazon', undefined, 'zebra', undefined, '', '@at', 'John', 'Abe', 'abc']; + inputArray.sort((value1, value2) => stringSortComparer(value1, value2, direction, columnDef, { cellValueCouldBeUndefined: true } as GridOption)); + expect(inputArray).toEqual(['', '@at', 'Abe', 'John', 'abc', 'amazon', 'zebra', undefined, undefined]); + }); + + it('should return a sorted descending array and move the undefined values to the end of the array when "cellValueCouldBeUndefined" is set in the grid options', () => { + // from MDN specification quote: All undefined elements are sorted to the end of the array. + const columnDef = { id: 'name', field: 'name' } as Column; + const direction = SortDirectionNumber.desc; + const inputArray = ['amazon', undefined, 'zebra', undefined, '', '@at', 'John', 'Abe', 'abc']; + inputArray.sort((value1, value2) => stringSortComparer(value1, value2, direction, columnDef, { cellValueCouldBeUndefined: true } as GridOption)); + expect(inputArray).toEqual(['zebra', 'amazon', 'abc', 'John', 'Abe', '@at', '', undefined, undefined]); + }); }); diff --git a/packages/common/src/sortComparers/dateUtilities.ts b/packages/common/src/sortComparers/dateUtilities.ts index 71be0d78b..8eebbd81f 100644 --- a/packages/common/src/sortComparers/dateUtilities.ts +++ b/packages/common/src/sortComparers/dateUtilities.ts @@ -1,12 +1,12 @@ import { mapMomentDateFormatWithFieldType } from '../services/utilities'; import { FieldType } from '../enums/fieldType.enum'; -import { Column, SortComparer } from '../interfaces/index'; +import { Column, GridOption, SortComparer } from '../interfaces/index'; import * as moment_ from 'moment-mini'; const moment = moment_; // patch to fix rollup "moment has no default export" issue, document here https://github.com/rollup/rollup/issues/670 -export function compareDates(value1: any, value2: any, sortDirection: number, sortColumn: Column, format: string | moment_.MomentBuiltinFormat, strict?: boolean) { +export function compareDates(value1: any, value2: any, sortDirection: number, sortColumn: Column, gridOptions: GridOption, format: string | moment_.MomentBuiltinFormat, strict?: boolean) { let diff = 0; - const checkForUndefinedValues = sortColumn && sortColumn.valueCouldBeUndefined || false; + const checkForUndefinedValues = sortColumn?.valueCouldBeUndefined ?? gridOptions?.cellValueCouldBeUndefined ?? false; if (value1 === null || value1 === '' || (checkForUndefinedValues && value1 === undefined) || !moment(value1, format, strict).isValid()) { diff = -1; @@ -25,10 +25,10 @@ export function compareDates(value1: any, value2: any, sortDirection: number, so export function getAssociatedDateSortComparer(fieldType: typeof FieldType[keyof typeof FieldType]): SortComparer { const FORMAT = (fieldType === FieldType.date) ? moment.ISO_8601 : mapMomentDateFormatWithFieldType(fieldType); - return (value1: any, value2: any, sortDirection: number, sortColumn: Column) => { + return (value1: any, value2: any, sortDirection: number, sortColumn: Column, gridOptions: GridOption) => { if (FORMAT === moment.ISO_8601) { - return compareDates(value1, value2, sortDirection, sortColumn, FORMAT, false); + return compareDates(value1, value2, sortDirection, sortColumn, gridOptions, FORMAT, false); } - return compareDates(value1, value2, sortDirection, sortColumn, FORMAT, true); + return compareDates(value1, value2, sortDirection, sortColumn, gridOptions, FORMAT, true); }; } diff --git a/packages/common/src/sortComparers/numericSortComparer.ts b/packages/common/src/sortComparers/numericSortComparer.ts index 1547ba4e2..34c265fcf 100644 --- a/packages/common/src/sortComparers/numericSortComparer.ts +++ b/packages/common/src/sortComparers/numericSortComparer.ts @@ -1,7 +1,7 @@ -import { Column, SortComparer } from '../interfaces/index'; +import { Column, GridOption, SortComparer } from '../interfaces/index'; -export const numericSortComparer: SortComparer = (value1: any, value2: any, sortDirection: number, sortColumn?: Column) => { - const checkForUndefinedValues = sortColumn && sortColumn.valueCouldBeUndefined || false; +export const numericSortComparer: SortComparer = (value1: any, value2: any, sortDirection: number, sortColumn?: Column, gridOptions?: GridOption) => { + const checkForUndefinedValues = sortColumn?.valueCouldBeUndefined ?? gridOptions?.cellValueCouldBeUndefined ?? false; const x = (isNaN(value1) || value1 === '' || value1 === null || (checkForUndefinedValues && value1 === undefined)) ? -99e+10 : parseFloat(value1); const y = (isNaN(value2) || value2 === '' || value2 === null || (checkForUndefinedValues && value2 === undefined)) ? -99e+10 : parseFloat(value2); return sortDirection * (x === y ? 0 : (x > y ? 1 : -1)); diff --git a/packages/common/src/sortComparers/objectStringSortComparer.ts b/packages/common/src/sortComparers/objectStringSortComparer.ts index a28a686cb..a98ff6810 100644 --- a/packages/common/src/sortComparers/objectStringSortComparer.ts +++ b/packages/common/src/sortComparers/objectStringSortComparer.ts @@ -1,13 +1,15 @@ -import { Column, SortComparer } from '../interfaces/index'; +import { Column, GridOption, SortComparer } from '../interfaces/index'; import { SortDirectionNumber } from '../enums/sortDirectionNumber.enum'; -export const objectStringSortComparer: SortComparer = (value1: any, value2: any, sortDirection: number | SortDirectionNumber, sortColumn: Column) => { +export const objectStringSortComparer: SortComparer = (value1: any, value2: any, sortDirection: number | SortDirectionNumber, sortColumn: Column, gridOptions?: GridOption) => { if (!sortColumn || !sortColumn.dataKey) { throw new Error('Sorting a "FieldType.object" requires you to provide the "dataKey" (object property name) of the object so that we can use it to sort correctly'); } const stringValue1 = value1?.hasOwnProperty(sortColumn.dataKey) ? value1[sortColumn.dataKey] : value1; const stringValue2 = value2?.hasOwnProperty(sortColumn.dataKey) ? value2[sortColumn.dataKey] : value2; + const checkForUndefinedValues = sortColumn?.valueCouldBeUndefined ?? gridOptions?.cellValueCouldBeUndefined ?? false; + if (sortDirection === undefined || sortDirection === null) { sortDirection = SortDirectionNumber.neutral; } @@ -17,9 +19,9 @@ export const objectStringSortComparer: SortComparer = (value1: any, value2: any, position = -99e+10; } else if (typeof value2 !== 'object') { position = 99e+10; - } else if (!stringValue1) { + } else if (stringValue1 === null || (checkForUndefinedValues && stringValue1 === undefined)) { position = -1; - } else if (!stringValue2) { + } else if (stringValue2 === null || (checkForUndefinedValues && stringValue2 === undefined)) { position = 1; } else if (stringValue1 === stringValue2) { position = 0; diff --git a/packages/common/src/sortComparers/sortUtilities.ts b/packages/common/src/sortComparers/sortUtilities.ts index cb24a2c5f..8534b56af 100644 --- a/packages/common/src/sortComparers/sortUtilities.ts +++ b/packages/common/src/sortComparers/sortUtilities.ts @@ -1,16 +1,16 @@ import { FieldType, SortDirectionNumber } from '../enums/index'; -import { Column } from '../interfaces/index'; +import { Column, GridOption } from '../interfaces/index'; import { SortComparers } from './index'; import { getAssociatedDateSortComparer } from './dateUtilities'; -export function sortByFieldType(fieldType: typeof FieldType[keyof typeof FieldType], value1: any, value2: any, sortDirection: number | SortDirectionNumber, sortColumn?: Column) { +export function sortByFieldType(fieldType: typeof FieldType[keyof typeof FieldType], value1: any, value2: any, sortDirection: number | SortDirectionNumber, sortColumn?: Column, gridOptions?: GridOption) { let sortResult = 0; switch (fieldType) { case FieldType.float: case FieldType.integer: case FieldType.number: - sortResult = SortComparers.numeric(value1, value2, sortDirection, sortColumn); + sortResult = SortComparers.numeric(value1, value2, sortDirection, sortColumn, gridOptions); break; case FieldType.date: case FieldType.dateIso: @@ -38,17 +38,17 @@ export function sortByFieldType(fieldType: typeof FieldType[keyof typeof FieldTy case FieldType.dateTimeUsShort: case FieldType.dateTimeUsShortAmPm: case FieldType.dateTimeUsShortAM_PM: - sortResult = getAssociatedDateSortComparer(fieldType).call(this, value1, value2, sortDirection, sortColumn); + sortResult = getAssociatedDateSortComparer(fieldType).call(this, value1, value2, sortDirection, sortColumn, gridOptions); break; case FieldType.object: - sortResult = SortComparers.objectString(value1, value2, sortDirection, sortColumn); + sortResult = SortComparers.objectString(value1, value2, sortDirection, sortColumn, gridOptions); break; case FieldType.string: case FieldType.text: case FieldType.password: case FieldType.readonly: default: - sortResult = SortComparers.string(value1, value2, sortDirection, sortColumn); + sortResult = SortComparers.string(value1, value2, sortDirection, sortColumn, gridOptions); break; } diff --git a/packages/common/src/sortComparers/stringSortComparer.ts b/packages/common/src/sortComparers/stringSortComparer.ts index c87be4c5b..e8e366e4f 100644 --- a/packages/common/src/sortComparers/stringSortComparer.ts +++ b/packages/common/src/sortComparers/stringSortComparer.ts @@ -1,12 +1,12 @@ -import { Column, SortComparer } from '../interfaces/index'; +import { Column, GridOption, SortComparer } from '../interfaces/index'; import { SortDirectionNumber } from '../enums/sortDirectionNumber.enum'; -export const stringSortComparer: SortComparer = (value1: any, value2: any, sortDirection: number | SortDirectionNumber, sortColumn?: Column) => { +export const stringSortComparer: SortComparer = (value1: any, value2: any, sortDirection: number | SortDirectionNumber, sortColumn?: Column, gridOptions?: GridOption) => { if (sortDirection === undefined || sortDirection === null) { sortDirection = SortDirectionNumber.neutral; } let position = 0; - const checkForUndefinedValues = sortColumn && sortColumn.valueCouldBeUndefined || false; + const checkForUndefinedValues = sortColumn?.valueCouldBeUndefined ?? gridOptions?.cellValueCouldBeUndefined ?? false; if (value1 === value2) { position = 0; diff --git a/packages/common/src/styles/material-svg-icons.scss b/packages/common/src/styles/material-svg-icons.scss index e5e61c93c..3194ce810 100644 --- a/packages/common/src/styles/material-svg-icons.scss +++ b/packages/common/src/styles/material-svg-icons.scss @@ -1,5 +1,6 @@ $icon-height: $icon-width; +/* Material Design Icons */ @include loadsvg( ".mdi.mdi-account", "M12,4A4,4 0 0,1 16,8A4,4 0 0,1 12,12A4,4 0 0,1 8,8A4,4 0 0,1 12,4M12,14C16.42,14 20,15.79 20,18V20H4V18C4,15.79 7.58,14 12,14Z", @@ -125,6 +126,11 @@ $icon-height: $icon-width; "M19.5,3.09L15,7.59V4H13V11H20V9H16.41L20.91,4.5L19.5,3.09M4,13V15H7.59L3.09,19.5L4.5,20.91L9,16.41V20H11V13H4Z", encodecolor($icon-color), $icon-height, $icon-width, inline-block); +@include loadsvg( + ".mdi.mdi-arrow-down", + "M11,4H13V16L18.5,10.5L19.92,11.92L12,19.84L4.08,11.92L5.5,10.5L11,16V4Z", + encodecolor($icon-color), $icon-height, $icon-width, inline-block); + @include loadsvg( ".mdi.mdi-arrow-down-bold-box", "M5,3H19A2,2 0 0,1 21,5V19A2,2 0 0,1 19,21H5A2,2 0 0,1 3,19V5A2,2 0 0,1 5,3M12,17L17,12H14V8H10V12H7L12,17Z", @@ -140,6 +146,11 @@ $icon-height: $icon-width; "M10,21V19H6.41L10.91,14.5L9.5,13.09L5,17.59V14H3V21H10M14.5,10.91L19,6.41V10H21V3H14V5H17.59L13.09,9.5L14.5,10.91Z", encodecolor($icon-color), $icon-height, $icon-width, inline-block); +@include loadsvg( + ".mdi.mdi-arrow-up", + "M13,20H11V8L5.5,13.5L4.08,12.08L12,4.16L19.92,12.08L18.5,13.5L13,8V20Z", + encodecolor($icon-color), $icon-height, $icon-width, inline-block); + @include loadsvg( ".mdi.mdi-calendar", "M19,19H5V8H19M16,1V3H8V1H6V3H5C3.89,3 3,3.89 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V5C21,3.89 20.1,3 19,3H18V1M17,12H12V17H17V12Z", diff --git a/packages/vanilla-bundle/dist-grid-bundle-zip/slickgrid-vanilla-bundle.zip b/packages/vanilla-bundle/dist-grid-bundle-zip/slickgrid-vanilla-bundle.zip index e3cc3215f..714467826 100644 Binary files a/packages/vanilla-bundle/dist-grid-bundle-zip/slickgrid-vanilla-bundle.zip and b/packages/vanilla-bundle/dist-grid-bundle-zip/slickgrid-vanilla-bundle.zip differ diff --git a/packages/vanilla-bundle/src/salesforce-global-grid-options.ts b/packages/vanilla-bundle/src/salesforce-global-grid-options.ts index cbcea6dcf..16ea5ba3c 100644 --- a/packages/vanilla-bundle/src/salesforce-global-grid-options.ts +++ b/packages/vanilla-bundle/src/salesforce-global-grid-options.ts @@ -4,6 +4,7 @@ import { GridOption, EventNamingStyle } from '@slickgrid-universal/common'; export const SalesforceGlobalGridOptions = { autoEdit: true, // true single click (false for double-click) autoCommitEdit: true, + cellValueCouldBeUndefined: true, compositeEditorOptions: { labels: { massSelectionButton: 'Apply to Selected & Save', @@ -36,6 +37,8 @@ export const SalesforceGlobalGridOptions = { }, headerMenu: { hideFreezeColumnsCommand: false, + iconSortAscCommand: 'fa fa-sort-amount-asc mdi mdi-arrow-up', + iconSortDescCommand: 'fa fa-sort-amount-desc mdi mdi-arrow-down', }, sanitizer: (dirtyHtml) => (dirtyHtml.replace(/(\b)(on\S+)(\s*)=|javascript:([^>]*)[^>]*|(<\s*)(\/*)script([<>]*).*(<\s*)(\/*)script([<>]*)/gi, '')), showCustomFooter: true,