Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/components/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@labkey/components",
"version": "2.288.0",
"version": "2.288.1",
"description": "Components, models, actions, and utility functions for LabKey applications and pages",
"sideEffects": false,
"files": [
Expand Down
5 changes: 5 additions & 0 deletions packages/components/releaseNotes/components.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
# @labkey/components
Components, models, actions, and utility functions for LabKey applications and pages.

### version 2.288.1
*Released*: 6 February 2023
- Issue 46618: Retain result row ordering in `formatSavedResults` and `formatResults` when processing results of `QuerySelect` queries.
- Add the `hasSortKey` property to the `QueryColumn` model. Now supported by `query-getQueryDetails.api`.

### version 2.288.0
*Released*: 1 February 2023
- Create components for `SamplesCreatedSuccessMessage` and `SamplesImportSuccessMessage`. Utilize `withRouter`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* limitations under the License.
*/
import React, { FC, PureComponent, ReactNode } from 'react';
import { fromJS, List, Map } from 'immutable';
import { List, Map } from 'immutable';
import { Filter, Query, Utils } from '@labkey/api';

import { SchemaQuery } from '../../../public/SchemaQuery';
Expand Down Expand Up @@ -230,12 +230,10 @@ export class QuerySelect extends PureComponent<QuerySelectOwnProps, State> {
if (request !== this.lastRequest) return;
delete this.lastRequest;

const models = fromJS(data.models[data.key]);

resolve(model.formatSavedResults(models, input));
resolve(model.formatSavedResults(data, input));

this.setState(() => ({
model: model.saveSearchResults(models),
model: model.saveSearchResults(data),
}));
} catch (error) {
const errorMsg = resolveErrorMessage(error) ?? 'Failed to retrieve search results.';
Expand Down
65 changes: 36 additions & 29 deletions packages/components/src/internal/components/forms/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { fromJS, List, Map, Record } from 'immutable';
import { fromJS, List, Map, OrderedMap, Record as ImmutableRecord } from 'immutable';
import { Filter, Query, Utils } from '@labkey/api';

import { QueryInfo } from '../../../public/QueryInfo';
Expand All @@ -36,45 +36,52 @@ import { QuerySelectOwnProps } from './QuerySelect';
import { resolveDetailFieldValue } from './utils';

function formatResults(model: QuerySelectModel, results: Map<string, any>, token?: string): SelectInputOption[] {
if (!model.queryInfo || !results) {
const { displayColumn, queryInfo, valueColumn } = model;

if (!queryInfo || !results) {
return [];
}

return results
.map(result => ({
label: (resolveDetailFieldValue(result.get(model.displayColumn)) ??
resolveDetailFieldValue(result.get(model.valueColumn))) as string,
value: result.getIn([model.valueColumn, 'value']),
}))
.sortBy(item => item.label, similaritySortFactory(token))
.toArray();
let options = results.map(result => ({
label: (resolveDetailFieldValue(result.get(displayColumn)) ??
resolveDetailFieldValue(result.get(valueColumn))) as string,
value: result.getIn([valueColumn, 'value']),
}));

// Issue 46618: If a sort key is applied, then skip sorting on the client to retain sort done on server.
if (!queryInfo.getColumn(displayColumn).hasSortKey) {
options = options.sortBy(item => item.label, similaritySortFactory(token));
}

return options.toArray();
}

/**
* Given a model this method returns "options" that are consumable by a ReactSelect.
* @param {QuerySelectModel} model for which results are formatted
* @param {Map<string, Map<string, any>>} results can be optionally supplied to override model searchResults
* @param {string} token an optional search token that will be used to sort the results
* @param model for which results are formatted
* @param result select rows result
* @param token an optional search token that will be used to sort the results
*/
function formatSavedResults(
model: QuerySelectModel,
results?: Map<string, Map<string, any>>,
token?: string
): SelectInputOption[] {
const { queryInfo, selectedItems, searchResults } = model;
function formatSavedResults(model: QuerySelectModel, result: ISelectRowsResult, token?: string): SelectInputOption[] {
const { queryInfo, selectedItems } = model;

if (!queryInfo) {
return [];
}

const filteredResults = (results !== undefined ? results : searchResults)
.filter((v, k) => !selectedItems.has(k))
.toMap();
const { key, orderedModels } = result;
const models = fromJS(result.models[key]);
const filteredResults = orderedModels[key]
.filter(k => !selectedItems.has(k))
.reduce((ordered, k) => ordered.set(k, models.get(k)), OrderedMap<string, any>());

return formatResults(model, filteredResults, token);
}

function saveSearchResults(model: QuerySelectModel, searchResults: Map<string, Map<string, any>>): QuerySelectModel {
function saveSearchResults(model: QuerySelectModel, result: ISelectRowsResult): QuerySelectModel {
const { key } = result;
const searchResults = fromJS(result.models[key]);

return model.merge({
allResults: model.allResults.merge(searchResults),
searchResults,
Expand Down Expand Up @@ -313,7 +320,7 @@ export interface QuerySelectModelProps {
}

export class QuerySelectModel
extends Record({
extends ImmutableRecord({
addExactFilter: true,
allResults: Map<string, Map<string, any>>(),
containerFilter: undefined,
Expand Down Expand Up @@ -354,8 +361,8 @@ export class QuerySelectModel
declare selectedItems: Map<string, any>;
declare valueColumn: string;

formatSavedResults(data?: Map<string, Map<string, any>>, token?: string): SelectInputOption[] {
return formatSavedResults(this, data, token);
formatSavedResults(result: ISelectRowsResult, token?: string): SelectInputOption[] {
return formatSavedResults(this, result, token);
}

getSelectedOptions(): SelectInputOption | SelectInputOption[] {
Expand Down Expand Up @@ -386,11 +393,11 @@ export class QuerySelectModel
return queryColumns;
}

saveSearchResults(data: Map<string, Map<string, any>>) {
return saveSearchResults(this, data);
saveSearchResults(result: ISelectRowsResult): QuerySelectModel {
return saveSearchResults(this, result);
}

setSelection(value: any) {
setSelection(value: any): QuerySelectModel {
return setSelection(this, value);
}

Expand Down
2 changes: 2 additions & 0 deletions packages/components/src/public/QueryColumn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ export class QueryColumn extends Record({
filterable: true,
format: undefined,
// friendlyType: undefined,
hasSortKey: false,
hidden: undefined,
inputType: undefined,
// isAutoIncrement: undefined, // DUPLICATE
Expand Down Expand Up @@ -163,6 +164,7 @@ export class QueryColumn extends Record({
declare filterable: boolean;
declare format: string;
// declare friendlyType: string;
declare hasSortKey: boolean;
declare hidden: boolean;
declare inputType: string;
// declare isAutoIncrement: boolean; // DUPLICATE
Expand Down