mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[Discover][ES|QL] Persist columns sorting inside SO (#180193)
- Closes https://github.com/elastic/kibana/issues/169345 ## Summary This PR allows to persist the configured columns sorting for ES|QL mode too. --------- Co-authored-by: Matthias Wilhelm <ankertal@gmail.com>
This commit is contained in:
parent
71788816cd
commit
357b6f8651
16 changed files with 330 additions and 74 deletions
|
@ -786,24 +786,21 @@ export const UnifiedDataTable = ({
|
|||
[sort, visibleColumns]
|
||||
);
|
||||
|
||||
const [inmemorySortingColumns, setInmemorySortingColumns] = useState([]);
|
||||
const onTableSort = useCallback(
|
||||
(sortingColumnsData) => {
|
||||
if (isSortEnabled) {
|
||||
if (isPlainRecord) {
|
||||
setInmemorySortingColumns(sortingColumnsData);
|
||||
} else if (onSort) {
|
||||
if (onSort) {
|
||||
onSort(sortingColumnsData.map(({ id, direction }: SortObj) => [id, direction]));
|
||||
}
|
||||
}
|
||||
},
|
||||
[onSort, isSortEnabled, isPlainRecord, setInmemorySortingColumns]
|
||||
[onSort, isSortEnabled]
|
||||
);
|
||||
|
||||
const sorting = useMemo(() => {
|
||||
if (isSortEnabled) {
|
||||
return {
|
||||
columns: isPlainRecord ? inmemorySortingColumns : sortingColumns,
|
||||
columns: sortingColumns,
|
||||
onSort: onTableSort,
|
||||
};
|
||||
}
|
||||
|
@ -811,7 +808,7 @@ export const UnifiedDataTable = ({
|
|||
columns: sortingColumns,
|
||||
onSort: () => {},
|
||||
};
|
||||
}, [isSortEnabled, sortingColumns, isPlainRecord, inmemorySortingColumns, onTableSort]);
|
||||
}, [isSortEnabled, sortingColumns, onTableSort]);
|
||||
|
||||
const canSetExpandedDoc = Boolean(setExpandedDoc && !!renderDocumentView);
|
||||
|
||||
|
|
|
@ -145,7 +145,10 @@ function buildEuiGridColumn({
|
|||
const column: EuiDataGridColumn = {
|
||||
id: columnName,
|
||||
schema: getSchemaByKbnType(columnType),
|
||||
isSortable: isSortEnabled && (isPlainRecord || dataViewField?.sortable === true),
|
||||
isSortable:
|
||||
isSortEnabled &&
|
||||
// TODO: would be great to have something like `sortable` flag for text based columns too
|
||||
((isPlainRecord && columnName !== '_source') || dataViewField?.sortable === true),
|
||||
display:
|
||||
showColumnTokens || headerRowHeight !== 1 ? (
|
||||
<DataTableColumnHeaderMemoized
|
||||
|
|
|
@ -18,17 +18,20 @@ describe('getDefaultSort function', function () {
|
|||
});
|
||||
|
||||
test('should return default sort for an data view with timeFieldName', function () {
|
||||
expect(getDefaultSort(stubDataView, 'desc', false)).toEqual([['@timestamp', 'desc']]);
|
||||
expect(getDefaultSort(stubDataView, 'asc', false)).toEqual([['@timestamp', 'asc']]);
|
||||
expect(getDefaultSort(stubDataView, 'desc', false, false)).toEqual([['@timestamp', 'desc']]);
|
||||
expect(getDefaultSort(stubDataView, 'asc', false, false)).toEqual([['@timestamp', 'asc']]);
|
||||
expect(getDefaultSort(stubDataView, 'asc', false, true)).toEqual([]);
|
||||
});
|
||||
|
||||
test('should return default sort for an data view without timeFieldName', function () {
|
||||
expect(getDefaultSort(stubDataViewWithoutTimeField, 'desc', false)).toEqual([]);
|
||||
expect(getDefaultSort(stubDataViewWithoutTimeField, 'asc', false)).toEqual([]);
|
||||
expect(getDefaultSort(stubDataViewWithoutTimeField, 'desc', false, false)).toEqual([]);
|
||||
expect(getDefaultSort(stubDataViewWithoutTimeField, 'asc', false, false)).toEqual([]);
|
||||
expect(getDefaultSort(stubDataViewWithoutTimeField, 'asc', false, true)).toEqual([]);
|
||||
});
|
||||
|
||||
test('should return empty sort for data view when time column is hidden', function () {
|
||||
expect(getDefaultSort(stubDataView, 'desc', true)).toEqual([]);
|
||||
expect(getDefaultSort(stubDataView, 'asc', true)).toEqual([]);
|
||||
expect(getDefaultSort(stubDataView, 'desc', true, false)).toEqual([]);
|
||||
expect(getDefaultSort(stubDataView, 'asc', true, false)).toEqual([]);
|
||||
expect(getDefaultSort(stubDataView, 'asc', true, true)).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -12,16 +12,21 @@ import { isSortable } from './get_sort';
|
|||
|
||||
/**
|
||||
* use in case the user didn't manually sort.
|
||||
* the default sort is returned depending of the data view
|
||||
* the default sort is returned depending on the data view or non for text based queries
|
||||
*/
|
||||
export function getDefaultSort(
|
||||
dataView: DataView | undefined,
|
||||
defaultSortOrder: string = 'desc',
|
||||
hidingTimeColumn: boolean = false
|
||||
hidingTimeColumn: boolean = false,
|
||||
isTextBasedQueryMode: boolean
|
||||
): SortOrder[] {
|
||||
if (isTextBasedQueryMode) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (
|
||||
dataView?.timeFieldName &&
|
||||
isSortable(dataView.timeFieldName, dataView) &&
|
||||
isSortable(dataView.timeFieldName, dataView, isTextBasedQueryMode) &&
|
||||
!hidingTimeColumn
|
||||
) {
|
||||
return [[dataView.timeFieldName, defaultSortOrder]];
|
||||
|
|
|
@ -12,39 +12,51 @@ import {
|
|||
stubDataViewWithoutTimeField,
|
||||
} from '@kbn/data-views-plugin/common/data_view.stub';
|
||||
|
||||
describe('docTable', function () {
|
||||
describe('getSort and getSortArray', function () {
|
||||
describe('getSort function', function () {
|
||||
test('should be a function', function () {
|
||||
expect(typeof getSort === 'function').toBeTruthy();
|
||||
});
|
||||
|
||||
test('should return an array of objects', function () {
|
||||
expect(getSort([['bytes', 'desc']], stubDataView)).toEqual([{ bytes: 'desc' }]);
|
||||
expect(getSort([['bytes', 'desc']], stubDataViewWithoutTimeField)).toEqual([
|
||||
expect(getSort([['bytes', 'desc']], stubDataView, false)).toEqual([{ bytes: 'desc' }]);
|
||||
expect(getSort([['bytes', 'desc']], stubDataView, true)).toEqual([{ bytes: 'desc' }]);
|
||||
expect(getSort([['bytes', 'desc']], stubDataViewWithoutTimeField, false)).toEqual([
|
||||
{ bytes: 'desc' },
|
||||
]);
|
||||
expect(getSort([['bytes', 'desc']], stubDataViewWithoutTimeField, true)).toEqual([
|
||||
{ bytes: 'desc' },
|
||||
]);
|
||||
});
|
||||
|
||||
test('should passthrough arrays of objects', () => {
|
||||
expect(getSort([{ bytes: 'desc' }], stubDataView)).toEqual([{ bytes: 'desc' }]);
|
||||
expect(getSort([{ bytes: 'desc' }], stubDataView, false)).toEqual([{ bytes: 'desc' }]);
|
||||
expect(getSort([{ bytes: 'desc' }], stubDataView, true)).toEqual([{ bytes: 'desc' }]);
|
||||
});
|
||||
|
||||
test('should return an empty array when passed an unsortable field', function () {
|
||||
expect(getSort([['non-sortable', 'asc']], stubDataView)).toEqual([]);
|
||||
expect(getSort([['lol_nope', 'asc']], stubDataView)).toEqual([]);
|
||||
expect(getSort([['non-sortable', 'asc']], stubDataView, false)).toEqual([]);
|
||||
expect(getSort([['non-sortable', 'asc']], stubDataView, true)).toEqual([
|
||||
{
|
||||
'non-sortable': 'asc',
|
||||
},
|
||||
]);
|
||||
expect(getSort([['lol_nope', 'asc']], stubDataView, false)).toEqual([]);
|
||||
|
||||
expect(getSort([['non-sortable', 'asc']], stubDataViewWithoutTimeField)).toEqual([]);
|
||||
expect(getSort([['non-sortable', 'asc']], stubDataViewWithoutTimeField, false)).toEqual([]);
|
||||
});
|
||||
|
||||
test('should return an empty array ', function () {
|
||||
expect(getSort([], stubDataView)).toEqual([]);
|
||||
expect(getSort([['foo', 'bar']], stubDataView)).toEqual([]);
|
||||
expect(getSort([{ foo: 'bar' }], stubDataView)).toEqual([]);
|
||||
expect(getSort([], stubDataView, false)).toEqual([]);
|
||||
expect(getSort([['foo', 'bar']], stubDataView, false)).toEqual([]);
|
||||
expect(getSort([{ foo: 'bar' }], stubDataView, false)).toEqual([]);
|
||||
expect(getSort([['foo', 'bar']], stubDataView, true)).toEqual([{ foo: 'bar' }]);
|
||||
expect(getSort([{ foo: 'bar' }], stubDataView, true)).toEqual([{ foo: 'bar' }]);
|
||||
});
|
||||
|
||||
test('should convert a legacy sort to an array of objects', function () {
|
||||
expect(getSort(['foo', 'desc'], stubDataView)).toEqual([{ foo: 'desc' }]);
|
||||
expect(getSort(['foo', 'asc'], stubDataView)).toEqual([{ foo: 'asc' }]);
|
||||
expect(getSort(['foo', 'desc'], stubDataView, false)).toEqual([{ foo: 'desc' }]);
|
||||
expect(getSort(['foo', 'asc'], stubDataView, false)).toEqual([{ foo: 'asc' }]);
|
||||
});
|
||||
});
|
||||
describe('getSortArray function', function () {
|
||||
|
@ -53,22 +65,28 @@ describe('docTable', function () {
|
|||
});
|
||||
|
||||
test('should return an array of arrays for sortable fields', function () {
|
||||
expect(getSortArray([['bytes', 'desc']], stubDataView)).toEqual([['bytes', 'desc']]);
|
||||
expect(getSortArray([['bytes', 'desc']], stubDataView, false)).toEqual([['bytes', 'desc']]);
|
||||
});
|
||||
|
||||
test('should return an array of arrays from an array of elasticsearch sort objects', function () {
|
||||
expect(getSortArray([{ bytes: 'desc' }], stubDataView)).toEqual([['bytes', 'desc']]);
|
||||
expect(getSortArray([{ bytes: 'desc' }], stubDataView, false)).toEqual([['bytes', 'desc']]);
|
||||
});
|
||||
|
||||
test('should sort by an empty array when an unsortable field is given', function () {
|
||||
expect(getSortArray([{ 'non-sortable': 'asc' }], stubDataView)).toEqual([]);
|
||||
expect(getSortArray([{ lol_nope: 'asc' }], stubDataView)).toEqual([]);
|
||||
expect(getSortArray([{ 'non-sortable': 'asc' }], stubDataViewWithoutTimeField)).toEqual([]);
|
||||
expect(getSortArray([{ 'non-sortable': 'asc' }], stubDataView, false)).toEqual([]);
|
||||
expect(getSortArray([{ 'non-sortable': 'asc' }], stubDataView, true)).toEqual([
|
||||
['non-sortable', 'asc'],
|
||||
]);
|
||||
expect(getSortArray([{ lol_nope: 'asc' }], stubDataView, false)).toEqual([]);
|
||||
expect(
|
||||
getSortArray([{ 'non-sortable': 'asc' }], stubDataViewWithoutTimeField, false)
|
||||
).toEqual([]);
|
||||
});
|
||||
|
||||
test('should return an empty array when passed an empty sort array', () => {
|
||||
expect(getSortArray([], stubDataView)).toEqual([]);
|
||||
expect(getSortArray([], stubDataViewWithoutTimeField)).toEqual([]);
|
||||
expect(getSortArray([], stubDataView, false)).toEqual([]);
|
||||
expect(getSortArray([], stubDataViewWithoutTimeField, false)).toEqual([]);
|
||||
expect(getSortArray([], stubDataViewWithoutTimeField, true)).toEqual([]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -14,20 +14,36 @@ export type SortPairObj = Record<string, string>;
|
|||
export type SortPair = SortOrder | SortPairObj;
|
||||
export type SortInput = SortPair | SortPair[];
|
||||
|
||||
export function isSortable(fieldName: string, dataView: DataView): boolean {
|
||||
export function isSortable(
|
||||
fieldName: string,
|
||||
dataView: DataView,
|
||||
isTextBasedQueryMode: boolean
|
||||
): boolean {
|
||||
if (isTextBasedQueryMode) {
|
||||
// in-memory sorting is used for text-based queries
|
||||
// would be great to have a way to determine if a text-based column is sortable
|
||||
return fieldName !== '_source';
|
||||
}
|
||||
const field = dataView.getFieldByName(fieldName);
|
||||
return !!(field && field.sortable);
|
||||
}
|
||||
|
||||
function createSortObject(sortPair: SortInput, dataView: DataView): SortPairObj | undefined {
|
||||
function createSortObject(
|
||||
sortPair: SortInput,
|
||||
dataView: DataView,
|
||||
isTextBasedQueryMode: boolean
|
||||
): SortPairObj | undefined {
|
||||
if (
|
||||
Array.isArray(sortPair) &&
|
||||
sortPair.length === 2 &&
|
||||
isSortable(String(sortPair[0]), dataView)
|
||||
isSortable(String(sortPair[0]), dataView, isTextBasedQueryMode)
|
||||
) {
|
||||
const [field, direction] = sortPair as SortOrder;
|
||||
return { [field]: direction };
|
||||
} else if (isPlainObject(sortPair) && isSortable(Object.keys(sortPair)[0], dataView)) {
|
||||
} else if (
|
||||
isPlainObject(sortPair) &&
|
||||
isSortable(Object.keys(sortPair)[0], dataView, isTextBasedQueryMode)
|
||||
) {
|
||||
return sortPair as SortPairObj;
|
||||
}
|
||||
}
|
||||
|
@ -43,16 +59,21 @@ export function isLegacySort(sort: SortPair[] | SortPair): sort is SortPair {
|
|||
* @param {array} sort two dimensional array [[fieldToSort, directionToSort]]
|
||||
* or an array of objects [{fieldToSort: directionToSort}]
|
||||
* @param {object} dataView used for determining default sort
|
||||
* @param {boolean} isTextBasedQueryMode
|
||||
* @returns Array<{object}> an array of sort objects
|
||||
*/
|
||||
export function getSort(sort: SortPair[] | SortPair, dataView: DataView): SortPairObj[] {
|
||||
export function getSort(
|
||||
sort: SortPair[] | SortPair,
|
||||
dataView: DataView,
|
||||
isTextBasedQueryMode: boolean
|
||||
): SortPairObj[] {
|
||||
if (Array.isArray(sort)) {
|
||||
if (isLegacySort(sort)) {
|
||||
// To stay compatible with legacy sort, which just supported a single sort field
|
||||
return [{ [sort[0]]: sort[1] }];
|
||||
}
|
||||
return sort
|
||||
.map((sortPair: SortPair) => createSortObject(sortPair, dataView))
|
||||
.map((sortPair: SortPair) => createSortObject(sortPair, dataView, isTextBasedQueryMode))
|
||||
.filter((sortPairObj) => typeof sortPairObj === 'object') as SortPairObj[];
|
||||
}
|
||||
return [];
|
||||
|
@ -62,8 +83,12 @@ export function getSort(sort: SortPair[] | SortPair, dataView: DataView): SortPa
|
|||
* compared to getSort it doesn't return an array of objects, it returns an array of arrays
|
||||
* [[fieldToSort: directionToSort]]
|
||||
*/
|
||||
export function getSortArray(sort: SortInput, dataView: DataView): SortOrder[] {
|
||||
return getSort(sort, dataView).reduce((acc: SortOrder[], sortPair) => {
|
||||
export function getSortArray(
|
||||
sort: SortInput,
|
||||
dataView: DataView,
|
||||
isTextBasedQueryMode: boolean
|
||||
): SortOrder[] {
|
||||
return getSort(sort, dataView, isTextBasedQueryMode).reduce((acc: SortOrder[], sortPair) => {
|
||||
const entries = Object.entries(sortPair);
|
||||
if (entries && entries[0]) {
|
||||
acc.push(entries[0]);
|
||||
|
|
|
@ -46,7 +46,7 @@ export function getSortForSearchSource({
|
|||
}
|
||||
|
||||
const { timeFieldName } = dataView;
|
||||
const sortPairs = getSort(sort, dataView);
|
||||
const sortPairs = getSort(sort, dataView, false); // text based request is not using search source
|
||||
|
||||
const sortForSearchSource = sortPairs.map((sortPair: Record<string, string>) => {
|
||||
if (timeFieldName && sortPair[timeFieldName]) {
|
||||
|
|
|
@ -418,7 +418,7 @@ function DiscoverDocumentsComponent({
|
|||
settings={grid}
|
||||
onFilter={onAddFilter as DocViewFilterFn}
|
||||
onSetColumns={onSetColumns}
|
||||
onSort={!isTextBasedQuery ? onSort : undefined}
|
||||
onSort={onSort}
|
||||
onResize={onResizeDataGrid}
|
||||
useNewFieldsApi={useNewFieldsApi}
|
||||
configHeaderRowHeight={3}
|
||||
|
@ -426,7 +426,7 @@ function DiscoverDocumentsComponent({
|
|||
onUpdateHeaderRowHeight={onUpdateHeaderRowHeight}
|
||||
rowHeightState={rowHeight}
|
||||
onUpdateRowHeight={onUpdateRowHeight}
|
||||
isSortEnabled={isTextBasedQuery ? Boolean(currentColumns.length) : true}
|
||||
isSortEnabled={true}
|
||||
isPlainRecord={isTextBasedQuery}
|
||||
rowsPerPageState={rowsPerPage ?? getDefaultRowsPerPage(services.uiSettings)}
|
||||
onUpdateRowsPerPage={onUpdateRowsPerPage}
|
||||
|
|
|
@ -48,7 +48,7 @@ export function getStateDefaults({
|
|||
|
||||
const query = searchSource.getField('query') || data.query.queryString.getDefaultQuery();
|
||||
const isTextBasedQueryMode = isTextBasedQuery(query);
|
||||
const sort = getSortArray(savedSearch.sort ?? [], dataView!);
|
||||
const sort = getSortArray(savedSearch.sort ?? [], dataView!, isTextBasedQueryMode);
|
||||
const columns = getDefaultColumns(savedSearch, uiSettings);
|
||||
const chartHidden = getChartHidden(storage, 'discover');
|
||||
|
||||
|
@ -58,7 +58,8 @@ export function getStateDefaults({
|
|||
? getDefaultSort(
|
||||
dataView,
|
||||
uiSettings.get(SORT_DEFAULT_ORDER_SETTING, 'desc'),
|
||||
uiSettings.get(DOC_HIDE_TIME_COLUMN_SETTING, false)
|
||||
uiSettings.get(DOC_HIDE_TIME_COLUMN_SETTING, false),
|
||||
isTextBasedQueryMode
|
||||
)
|
||||
: sort,
|
||||
columns,
|
||||
|
|
|
@ -37,7 +37,9 @@ export function getDataViewAppState(
|
|||
);
|
||||
}
|
||||
|
||||
if (query && isOfAggregateQueryType(query)) {
|
||||
const isTextBasedQueryMode = isOfAggregateQueryType(query);
|
||||
|
||||
if (isTextBasedQueryMode) {
|
||||
columns = [];
|
||||
}
|
||||
|
||||
|
@ -45,7 +47,7 @@ export function getDataViewAppState(
|
|||
// filter out sorting by timeField in case it is set. data views without timeField don't
|
||||
// prepend this field in the table, so in legacy grid you would need to add this column to
|
||||
// remove sorting
|
||||
let nextSort = getSortArray(currentSort, nextDataView).filter((value) => {
|
||||
let nextSort = getSortArray(currentSort, nextDataView, isTextBasedQueryMode).filter((value) => {
|
||||
return nextDataView.timeFieldName || value[0] !== currentDataView.timeFieldName;
|
||||
});
|
||||
|
||||
|
|
|
@ -57,7 +57,7 @@ export function TableHeader({
|
|||
sortOrder={
|
||||
sortOrder.length
|
||||
? sortOrder
|
||||
: getDefaultSort(dataView, defaultSortOrder, hideTimeColumn)
|
||||
: getDefaultSort(dataView, defaultSortOrder, hideTimeColumn, false)
|
||||
}
|
||||
onMoveColumn={onMoveColumn}
|
||||
onRemoveColumn={onRemoveColumn}
|
||||
|
|
|
@ -389,8 +389,12 @@ export class SavedSearchEmbeddable
|
|||
}
|
||||
};
|
||||
|
||||
private getSort(sort: SortPair[] | undefined, dataView?: DataView) {
|
||||
return getSortForEmbeddable(sort, dataView, this.services.uiSettings);
|
||||
private getSort(
|
||||
sort: SortPair[] | undefined,
|
||||
dataView: DataView | undefined,
|
||||
isTextBasedQueryMode: boolean
|
||||
) {
|
||||
return getSortForEmbeddable(sort, dataView, this.services.uiSettings, isTextBasedQueryMode);
|
||||
}
|
||||
|
||||
private initializeSearchEmbeddableProps() {
|
||||
|
@ -417,7 +421,7 @@ export class SavedSearchEmbeddable
|
|||
filters: savedSearch.searchSource.getField('filter') as Filter[],
|
||||
dataView,
|
||||
isLoading: false,
|
||||
sort: this.getSort(savedSearch.sort, dataView),
|
||||
sort: this.getSort(savedSearch.sort, dataView, this.isTextBasedSearch(savedSearch)),
|
||||
rows: [],
|
||||
searchDescription: savedSearch.description,
|
||||
description: savedSearch.description,
|
||||
|
@ -573,7 +577,11 @@ export class SavedSearchEmbeddable
|
|||
);
|
||||
|
||||
searchProps.columns = columnState.columns || [];
|
||||
searchProps.sort = this.getSort(this.input.sort || savedSearch.sort, searchProps?.dataView);
|
||||
searchProps.sort = this.getSort(
|
||||
this.input.sort || savedSearch.sort,
|
||||
searchProps?.dataView,
|
||||
this.isTextBasedSearch(savedSearch)
|
||||
);
|
||||
searchProps.sharedItemTitle = this.panelTitleInternal;
|
||||
searchProps.searchTitle = this.panelTitleInternal;
|
||||
searchProps.rowHeightState = this.input.rowHeight ?? savedSearch.rowHeight;
|
||||
|
|
|
@ -13,35 +13,56 @@ import {
|
|||
} from '@kbn/data-views-plugin/common/data_view.stub';
|
||||
import { uiSettingsMock } from '../../__mocks__/ui_settings';
|
||||
|
||||
describe('docTable', function () {
|
||||
describe('getSortForEmbeddable', function () {
|
||||
describe('getSortForEmbeddable function', function () {
|
||||
test('should return an array of arrays for sortable fields', function () {
|
||||
expect(getSortForEmbeddable([['bytes', 'desc']], stubDataView)).toEqual([['bytes', 'desc']]);
|
||||
expect(getSortForEmbeddable([['bytes', 'desc']], stubDataView, undefined, false)).toEqual([
|
||||
['bytes', 'desc'],
|
||||
]);
|
||||
expect(getSortForEmbeddable([['bytes', 'desc']], stubDataView, undefined, true)).toEqual([
|
||||
['bytes', 'desc'],
|
||||
]);
|
||||
});
|
||||
|
||||
test('should return an array of arrays from an array of elasticsearch sort objects', function () {
|
||||
expect(getSortForEmbeddable([{ bytes: 'desc' }], stubDataView)).toEqual([['bytes', 'desc']]);
|
||||
expect(getSortForEmbeddable([{ bytes: 'desc' }], stubDataView, undefined, false)).toEqual([
|
||||
['bytes', 'desc'],
|
||||
]);
|
||||
});
|
||||
|
||||
test('should sort by an empty array when an unsortable field is given', function () {
|
||||
expect(getSortForEmbeddable([{ 'non-sortable': 'asc' }], stubDataView)).toEqual([]);
|
||||
expect(getSortForEmbeddable([{ lol_nope: 'asc' }], stubDataView)).toEqual([]);
|
||||
expect(
|
||||
getSortForEmbeddable([{ 'non-sortable': 'asc' }], stubDataViewWithoutTimeField)
|
||||
getSortForEmbeddable([{ 'non-sortable': 'asc' }], stubDataView, undefined, false)
|
||||
).toEqual([]);
|
||||
expect(
|
||||
getSortForEmbeddable([{ 'non-sortable': 'asc' }], stubDataView, undefined, true)
|
||||
).toEqual([['non-sortable', 'asc']]);
|
||||
expect(getSortForEmbeddable([{ lol_nope: 'asc' }], stubDataView, undefined, false)).toEqual(
|
||||
[]
|
||||
);
|
||||
expect(
|
||||
getSortForEmbeddable(
|
||||
[{ 'non-sortable': 'asc' }],
|
||||
stubDataViewWithoutTimeField,
|
||||
undefined,
|
||||
false
|
||||
)
|
||||
).toEqual([]);
|
||||
});
|
||||
|
||||
test('should return an empty array when passed an empty sort array', () => {
|
||||
expect(getSortForEmbeddable([], stubDataView)).toEqual([]);
|
||||
expect(getSortForEmbeddable([], stubDataViewWithoutTimeField)).toEqual([]);
|
||||
expect(getSortForEmbeddable([], stubDataView, undefined, false)).toEqual([]);
|
||||
expect(getSortForEmbeddable([], stubDataView, undefined, true)).toEqual([]);
|
||||
expect(getSortForEmbeddable([], stubDataViewWithoutTimeField, undefined, false)).toEqual([]);
|
||||
});
|
||||
|
||||
test('should provide fallback results', () => {
|
||||
expect(getSortForEmbeddable(undefined)).toEqual([]);
|
||||
expect(getSortForEmbeddable(undefined, stubDataView)).toEqual([]);
|
||||
expect(getSortForEmbeddable(undefined, stubDataView, uiSettingsMock)).toEqual([
|
||||
expect(getSortForEmbeddable(undefined, undefined, undefined, false)).toEqual([]);
|
||||
expect(getSortForEmbeddable(undefined, stubDataView, undefined, false)).toEqual([]);
|
||||
expect(getSortForEmbeddable(undefined, stubDataView, uiSettingsMock, false)).toEqual([
|
||||
['@timestamp', 'desc'],
|
||||
]);
|
||||
expect(getSortForEmbeddable(undefined, stubDataView, uiSettingsMock, true)).toEqual([]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -16,9 +16,10 @@ import { getDefaultSort, getSortArray, SortInput } from '../../../common/utils/s
|
|||
* sorting for embeddable, like getSortArray,but returning a default in the case the given sort or dataView is not valid
|
||||
*/
|
||||
export function getSortForEmbeddable(
|
||||
sort?: SortInput,
|
||||
dataView?: DataView,
|
||||
uiSettings?: IUiSettingsClient
|
||||
sort: SortInput | undefined,
|
||||
dataView: DataView | undefined,
|
||||
uiSettings: IUiSettingsClient | undefined,
|
||||
isTextBasedQueryMode: boolean
|
||||
): SortOrder[] {
|
||||
if (!sort || !sort.length || !dataView) {
|
||||
if (!uiSettings) {
|
||||
|
@ -26,7 +27,7 @@ export function getSortForEmbeddable(
|
|||
}
|
||||
const defaultSortOrder = uiSettings.get(SORT_DEFAULT_ORDER_SETTING, 'desc');
|
||||
const hidingTimeColumn = uiSettings.get(DOC_HIDE_TIME_COLUMN_SETTING, false);
|
||||
return getDefaultSort(dataView, defaultSortOrder, hidingTimeColumn);
|
||||
return getDefaultSort(dataView, defaultSortOrder, hidingTimeColumn, isTextBasedQueryMode);
|
||||
}
|
||||
return getSortArray(sort, dataView);
|
||||
return getSortArray(sort, dataView, isTextBasedQueryMode);
|
||||
}
|
||||
|
|
|
@ -19,11 +19,14 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
const monacoEditor = getService('monacoEditor');
|
||||
const security = getService('security');
|
||||
const retry = getService('retry');
|
||||
const browser = getService('browser');
|
||||
const find = getService('find');
|
||||
const esql = getService('esql');
|
||||
const dashboardAddPanel = getService('dashboardAddPanel');
|
||||
const PageObjects = getPageObjects([
|
||||
'common',
|
||||
'discover',
|
||||
'dashboard',
|
||||
'header',
|
||||
'timePicker',
|
||||
'unifiedFieldList',
|
||||
|
@ -81,7 +84,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
expect(await testSubjects.exists('discoverQueryHits')).to.be(true);
|
||||
expect(await testSubjects.exists('discoverAlertsButton')).to.be(true);
|
||||
expect(await testSubjects.exists('shareTopNavButton')).to.be(true);
|
||||
expect(await testSubjects.exists('dataGridColumnSortingButton')).to.be(false);
|
||||
expect(await testSubjects.exists('dataGridColumnSortingButton')).to.be(true);
|
||||
expect(await testSubjects.exists('docTableExpandToggleColumn')).to.be(true);
|
||||
expect(await testSubjects.exists('fieldListFiltersFieldTypeFilterToggle')).to.be(true);
|
||||
await testSubjects.click('field-@message-showDetails');
|
||||
|
@ -324,5 +327,174 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
await historyItem.findByTestSubject('TextBasedLangEditor-queryHistory-error');
|
||||
});
|
||||
});
|
||||
|
||||
describe('sorting', () => {
|
||||
it('should sort correctly', async () => {
|
||||
const savedSearchName = 'testSorting';
|
||||
|
||||
await PageObjects.discover.selectTextBaseLang();
|
||||
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||
await PageObjects.discover.waitUntilSearchingHasFinished();
|
||||
|
||||
const testQuery = 'from logstash-* | sort @timestamp | limit 100';
|
||||
await monacoEditor.setCodeEditorValue(testQuery);
|
||||
await testSubjects.click('querySubmitButton');
|
||||
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||
await PageObjects.discover.waitUntilSearchingHasFinished();
|
||||
await PageObjects.unifiedFieldList.waitUntilSidebarHasLoaded();
|
||||
|
||||
await PageObjects.unifiedFieldList.clickFieldListItemAdd('bytes');
|
||||
|
||||
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||
await PageObjects.discover.waitUntilSearchingHasFinished();
|
||||
|
||||
await retry.waitFor('first cell contains an initial value', async () => {
|
||||
const cell = await dataGrid.getCellElement(0, 2);
|
||||
const text = await cell.getVisibleText();
|
||||
return text === '1,623';
|
||||
});
|
||||
|
||||
expect(await testSubjects.getVisibleText('dataGridColumnSortingButton')).to.be(
|
||||
'Sort fields'
|
||||
);
|
||||
|
||||
await dataGrid.clickDocSortDesc('bytes', 'Sort High-Low');
|
||||
|
||||
await PageObjects.discover.waitUntilSearchingHasFinished();
|
||||
|
||||
await retry.waitFor('first cell contains the highest value', async () => {
|
||||
const cell = await dataGrid.getCellElement(0, 2);
|
||||
const text = await cell.getVisibleText();
|
||||
return text === '483';
|
||||
});
|
||||
|
||||
expect(await testSubjects.getVisibleText('dataGridColumnSortingButton')).to.be(
|
||||
'Sort fields\n1'
|
||||
);
|
||||
|
||||
await PageObjects.discover.saveSearch(savedSearchName);
|
||||
|
||||
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||
await PageObjects.discover.waitUntilSearchingHasFinished();
|
||||
|
||||
await retry.waitFor('first cell contains the same highest value', async () => {
|
||||
const cell = await dataGrid.getCellElement(0, 2);
|
||||
const text = await cell.getVisibleText();
|
||||
return text === '483';
|
||||
});
|
||||
|
||||
await browser.refresh();
|
||||
|
||||
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||
await PageObjects.discover.waitUntilSearchingHasFinished();
|
||||
|
||||
await retry.waitFor('first cell contains the same highest value after reload', async () => {
|
||||
const cell = await dataGrid.getCellElement(0, 2);
|
||||
const text = await cell.getVisibleText();
|
||||
return text === '483';
|
||||
});
|
||||
|
||||
await PageObjects.discover.clickNewSearchButton();
|
||||
|
||||
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||
await PageObjects.discover.waitUntilSearchingHasFinished();
|
||||
|
||||
await PageObjects.discover.loadSavedSearch(savedSearchName);
|
||||
|
||||
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||
await PageObjects.discover.waitUntilSearchingHasFinished();
|
||||
|
||||
await retry.waitFor(
|
||||
'first cell contains the same highest value after reopening',
|
||||
async () => {
|
||||
const cell = await dataGrid.getCellElement(0, 2);
|
||||
const text = await cell.getVisibleText();
|
||||
return text === '483';
|
||||
}
|
||||
);
|
||||
|
||||
await dataGrid.clickDocSortDesc('bytes', 'Sort Low-High');
|
||||
|
||||
await PageObjects.discover.waitUntilSearchingHasFinished();
|
||||
|
||||
await retry.waitFor('first cell contains the lowest value', async () => {
|
||||
const cell = await dataGrid.getCellElement(0, 2);
|
||||
const text = await cell.getVisibleText();
|
||||
return text === '0';
|
||||
});
|
||||
|
||||
expect(await testSubjects.getVisibleText('dataGridColumnSortingButton')).to.be(
|
||||
'Sort fields\n1'
|
||||
);
|
||||
|
||||
await PageObjects.unifiedFieldList.clickFieldListItemAdd('extension');
|
||||
|
||||
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||
await PageObjects.discover.waitUntilSearchingHasFinished();
|
||||
|
||||
await dataGrid.clickDocSortDesc('extension', 'Sort A-Z');
|
||||
|
||||
await retry.waitFor('first cell contains the lowest value for extension', async () => {
|
||||
const cell = await dataGrid.getCellElement(0, 3);
|
||||
const text = await cell.getVisibleText();
|
||||
return text === 'css';
|
||||
});
|
||||
|
||||
expect(await testSubjects.getVisibleText('dataGridColumnSortingButton')).to.be(
|
||||
'Sort fields\n2'
|
||||
);
|
||||
|
||||
await browser.refresh();
|
||||
|
||||
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||
await PageObjects.discover.waitUntilSearchingHasFinished();
|
||||
|
||||
await retry.waitFor('first cell contains the same lowest value after reload', async () => {
|
||||
const cell = await dataGrid.getCellElement(0, 2);
|
||||
const text = await cell.getVisibleText();
|
||||
return text === '0';
|
||||
});
|
||||
|
||||
await retry.waitFor(
|
||||
'first cell contains the same lowest value for extension after reload',
|
||||
async () => {
|
||||
const cell = await dataGrid.getCellElement(0, 3);
|
||||
const text = await cell.getVisibleText();
|
||||
return text === 'css';
|
||||
}
|
||||
);
|
||||
|
||||
await PageObjects.discover.saveSearch(savedSearchName);
|
||||
|
||||
await PageObjects.common.navigateToApp('dashboard');
|
||||
await PageObjects.dashboard.clickNewDashboard();
|
||||
await PageObjects.timePicker.setDefaultAbsoluteRange();
|
||||
await dashboardAddPanel.clickOpenAddPanel();
|
||||
await dashboardAddPanel.addSavedSearch(savedSearchName);
|
||||
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||
|
||||
await retry.waitFor(
|
||||
'first cell contains the same lowest value as dashboard panel',
|
||||
async () => {
|
||||
const cell = await dataGrid.getCellElement(0, 2);
|
||||
const text = await cell.getVisibleText();
|
||||
return text === '0';
|
||||
}
|
||||
);
|
||||
|
||||
await retry.waitFor(
|
||||
'first cell contains the lowest value for extension as dashboard panel',
|
||||
async () => {
|
||||
const cell = await dataGrid.getCellElement(0, 3);
|
||||
const text = await cell.getVisibleText();
|
||||
return text === 'css';
|
||||
}
|
||||
);
|
||||
|
||||
expect(await testSubjects.getVisibleText('dataGridColumnSortingButton')).to.be(
|
||||
'Sort fields\n2'
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -83,7 +83,7 @@ export class DataGridService extends FtrService {
|
|||
}
|
||||
|
||||
private getCellElementSelector(rowIndex: number = 0, columnIndex: number = 0) {
|
||||
return `[data-test-subj="euiDataGridBody"] [data-test-subj="dataGridRowCell"][data-gridcell-column-index="${columnIndex}"][data-gridcell-row-index="${rowIndex}"]`;
|
||||
return `[data-test-subj="euiDataGridBody"] [data-test-subj="dataGridRowCell"][data-gridcell-column-index="${columnIndex}"][data-gridcell-visible-row-index="${rowIndex}"]`;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue