Skip to content

Commit b7b9cec

Browse files
committed
merge from develop
2 parents addb875 + 2933121 commit b7b9cec

9 files changed

Lines changed: 54 additions & 41 deletions

File tree

packages/components/package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/components/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@labkey/components",
3-
"version": "7.16.1-fb-relativeDayFilter.1",
3+
"version": "7.16.1",
44
"description": "Components, models, actions, and utility functions for LabKey applications and pages",
55
"sideEffects": false,
66
"files": [

packages/components/releaseNotes/components.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
# @labkey/components
22
Components, models, actions, and utility functions for LabKey applications and pages
33

4-
### version 7.X
5-
*Released*: X 2026
4+
### version 7.16.2
5+
*Released*: 11 February 2026
66
- GitHub Issue 779: Cannot Edit Relative Dates in Sample Finder
77
- Allow intermediate editing state for relative date values in DatePickerInput
88

9+
### version 7.16.1
10+
*Released*: 11 February 2026
11+
- Query: sort columns where name starts with '+'
12+
913
### version 7.16.0
1014
*Released*: 5 February 2026
1115
- File import warnings for cross type sample import case

packages/components/src/internal/renderers.tsx

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ import { usePortalRef } from './hooks';
4747
import { MenuDivider, MenuItem } from './dropdowns';
4848
import { LabelOverlay } from './components/forms/LabelOverlay';
4949
import { DOMAIN_FIELD } from './components/forms/DomainFieldHelpTipContents';
50+
import { SORT_ASC, SORT_DESC } from '../public/QuerySort';
5051

5152
export function isFilterColumnNameMatch(filter: Filter.IFilter, col: QueryColumn): boolean {
5253
return filter.getColumnName() === col.name || filter.getColumnName() === col.resolveFieldKey();
@@ -108,11 +109,11 @@ export const EditableColumnTitle: FC<EditableColumnTitleProps> = memo(props => {
108109
return (
109110
<input
110111
autoFocus
111-
ref={titleInput}
112112
defaultValue={title}
113-
onKeyDown={onKeyDown}
114-
onChange={onTitleChange}
115113
onBlur={onEditFinish}
114+
onChange={onTitleChange}
115+
onKeyDown={onKeyDown}
116+
ref={titleInput}
116117
/>
117118
);
118119
}
@@ -318,8 +319,8 @@ const HeaderCellDropdownMenu: FC<HeaderCellDropdownMenuProps> = memo(props => {
318319
)}
319320
<DisableableMenuItem
320321
disabled={!(handleHideColumn && !!model)}
321-
onClick={hideColumn}
322322
disabledMessage={APP_FIELD_CANNOT_BE_REMOVED_MESSAGE}
323+
onClick={hideColumn}
323324
>
324325
<span className="fa fa-eye-slash grid-panel__menu-icon" />
325326
Hide Column
@@ -397,18 +398,19 @@ export const HeaderCellDropdown: FC<HeaderCellDropdownProps> = memo(props => {
397398
const colQuerySortDir =
398399
model?.sorts?.find(sort => sort.fieldKey === queryColumn.resolveFieldKey())?.dir ??
399400
view?.sorts?.find(sort => sort.fieldKey === queryColumn.resolveFieldKey())?.dir;
400-
const isSortAsc = queryColumn.sorts === '+' || colQuerySortDir === '+' || colQuerySortDir === '';
401-
const isSortDesc = queryColumn.sorts === '-' || colQuerySortDir === '-';
401+
const sortDir = queryColumn.sorts || colQuerySortDir;
402+
const isSortAsc = sortDir === SORT_ASC;
403+
const isSortDesc = sortDir === SORT_DESC;
402404

403405
return (
404406
<div className={GRID_HEADER_CELL_BODY} onClick={click}>
405407
<div className="grid-header-cell__title-wrapper">
406408
<EditableColumnTitle
407409
column={queryColumn}
408-
onChange={onColumnTitleUpdate}
409410
editing={editingTitle}
410-
onCancel={cancelEditTitle}
411411
hideToolTip={!!column.helpTipRenderer}
412+
onCancel={cancelEditTitle}
413+
onChange={onColumnTitleUpdate}
412414
/>
413415

414416
{!editingTitle && colFilters?.length > 0 && (
@@ -426,10 +428,10 @@ export const HeaderCellDropdown: FC<HeaderCellDropdownProps> = memo(props => {
426428
{!editingTitle && column.helpTipRenderer && (
427429
<LabelHelpTip
428430
placement="bottom"
429-
title={column.title}
430431
popoverClassName={column.helpTipRenderer === DOMAIN_FIELD ? undefined : 'label-help-arrow-left'}
432+
title={column.title}
431433
>
432-
<HelpTipRenderer type={column.helpTipRenderer} column={queryColumn} />
434+
<HelpTipRenderer column={queryColumn} type={column.helpTipRenderer} />
433435
</LabelHelpTip>
434436
)}
435437
</div>
@@ -445,8 +447,8 @@ export const HeaderCellDropdown: FC<HeaderCellDropdownProps> = memo(props => {
445447
isSortAsc={isSortAsc}
446448
isSortDesc={isSortDesc}
447449
model={model}
448-
open={open}
449450
onEditTitleClicked={editTitle}
451+
open={open}
450452
queryColumn={queryColumn}
451453
setOpen={setOpen}
452454
/>
@@ -476,8 +478,8 @@ export const HeaderSelectionCell: FC<HeaderSelectionCellProps> = memo(props => {
476478

477479
return (
478480
<input
479-
className={className}
480481
checked={selectedState === GRID_CHECKBOX_OPTIONS.ALL}
482+
className={className}
481483
disabled={disabled}
482484
onChange={handleSelection}
483485
ref={checkboxRef}

packages/components/src/public/QueryColumn.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import { SAMPLES_WITH_TYPES_FILTER } from '../internal/components/samples/consta
1919
import { SchemaQuery } from './SchemaQuery';
2020
import { IQueryColumn } from './IQueryColumn';
2121
import { PropDescType } from '../internal/components/domainproperties/PropDescType';
22+
import { SortDirection } from './QuerySort';
2223

2324
export enum Operation {
2425
insert = 'insert',
@@ -189,7 +190,7 @@ export class QueryColumn implements IQueryColumn {
189190
declare detailRenderer: string;
190191
declare helpTipRenderer: string;
191192
declare inputRenderer: string;
192-
declare sorts: '+' | '-';
193+
declare sorts: SortDirection;
193194
declare removeFromViews: boolean; // strips this column from all ViewInfo definitions
194195
declare removeFromFormInput: boolean; // strips this column from QueryFormInputs
195196
declare units: string;

packages/components/src/public/QueryModel/GridPanel.tsx

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import React, {
1111
} from 'react';
1212
import classNames from 'classnames';
1313
import { fromJS, List, Map, Set } from 'immutable';
14-
import { Filter, getServerContext, Query } from '@labkey/api';
14+
import { Filter, Query } from '@labkey/api';
1515

1616
import { EXPORT_TYPES, GRID_CHECKBOX_OPTIONS, GRID_SELECTION_INDEX } from '../../internal/constants';
1717
import { HeaderCellDropdown, HeaderSelectionCell, isFilterColumnNameMatch } from '../../internal/renderers';
@@ -25,15 +25,15 @@ import {
2525
saveSessionView,
2626
} from '../../internal/actions';
2727

28-
import { hasServerContext, useServerContext } from '../../internal/components/base/ServerContext';
28+
import { useServerContext } from '../../internal/components/base/ServerContext';
2929

3030
import { Pagination } from '../../internal/components/pagination/Pagination';
3131

3232
import { ViewInfo } from '../../internal/ViewInfo';
3333

3434
import { QueryColumn } from '../QueryColumn';
3535

36-
import { QuerySort } from '../QuerySort';
36+
import { QuerySort, SortDirection } from '../QuerySort';
3737

3838
import { GridColumn } from '../../internal/components/base/models/GridColumn';
3939

@@ -711,12 +711,11 @@ export class GridPanel<T = {}> extends PureComponent<Props<T>, State> {
711711
this.setState({ showFilterModalFieldKey: undefined });
712712
};
713713

714-
sortColumn = (column: QueryColumn, direction?: string): void => {
714+
sortColumn = (column: QueryColumn, direction?: SortDirection): void => {
715715
const fieldKey = column.resolveFieldKey(); // resolveFieldKey because of Issue 34627
716716

717717
if (direction) {
718-
const dir = direction === '+' ? '' : '-'; // Sort Action only uses '-' and ''
719-
const sort = new QuerySort({ fieldKey, dir });
718+
const sort = new QuerySort({ dir: direction, fieldKey });
720719
this.handleSortChange({ type: ChangeType.add }, sort);
721720
} else {
722721
const actionIndex = this.state.actionValues.findIndex(

packages/components/src/public/QueryModel/QueryModel.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ describe('QueryModel', () => {
140140
let model = new QueryModel({ schemaQuery: SCHEMA_QUERY, sorts });
141141
expect(() => model.sortString).toThrow('Cannot construct sort string, no QueryInfo available');
142142
model = model.mutate({ queryInfo: QUERY_INFO });
143-
expect(model.sortString).toEqual('-RowId,Data');
143+
expect(model.sortString).toEqual('-RowId,+Data');
144144
});
145145

146146
test('Columns', () => {

packages/components/src/public/QueryModel/QueryModel.ts

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -59,16 +59,8 @@ function offsetFromString(rowsPerPage: number, pageStr: string): number {
5959
return offset >= 0 ? offset : 0;
6060
}
6161

62-
export function querySortFromString(sortStr: string): QuerySort {
63-
if (sortStr.startsWith('-')) {
64-
return new QuerySort({ dir: '-', fieldKey: sortStr.slice(1) });
65-
} else {
66-
return new QuerySort({ fieldKey: sortStr });
67-
}
68-
}
69-
7062
function querySortsFromString(sortsStr: string): QuerySort[] {
71-
return sortsStr?.split(',').map(querySortFromString);
63+
return sortsStr?.split(',').map(QuerySort.fromString);
7264
}
7365

7466
function searchFiltersFromString(searchStr: string): Filter.IFilter[] {
@@ -1281,7 +1273,7 @@ export function getSettingsFromLocalStorage(id: string, containerPath: string):
12811273
const filterArray = savedSettings.filterArray?.map(f =>
12821274
Filter.create(f.columnName, f.value, Filter.getFilterTypeForURLSuffix(f.type))
12831275
);
1284-
const sorts = savedSettings.sorts?.map(s => querySortFromString(s));
1276+
const sorts = savedSettings.sorts?.map(QuerySort.fromString);
12851277

12861278
return {
12871279
filterArray: filterArray ?? [],
Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,33 @@
1+
export const SORT_ASC = '+';
2+
export const SORT_DESC = '-';
3+
4+
export type SortDirection = typeof SORT_ASC | typeof SORT_DESC;
5+
16
export interface QuerySortJson {
2-
dir: string;
7+
dir?: SortDirection;
38
fieldKey: string;
49
}
510

611
export class QuerySort implements QuerySortJson {
7-
declare dir: string;
8-
declare fieldKey: string;
12+
public dir?: SortDirection;
13+
public fieldKey: string;
14+
15+
static fromString(sortStr: string): QuerySort {
16+
if (sortStr.startsWith(SORT_DESC)) {
17+
return new QuerySort({ dir: SORT_DESC, fieldKey: sortStr.slice(1) });
18+
} else if (sortStr.startsWith(SORT_ASC)) {
19+
return new QuerySort({ dir: SORT_ASC, fieldKey: sortStr.slice(1) });
20+
}
21+
22+
return new QuerySort({ fieldKey: sortStr });
23+
}
924

1025
constructor(props: Partial<QuerySort>) {
11-
Object.assign(this, { dir: '' }, props);
26+
this.dir = props.dir === SORT_DESC ? SORT_DESC : SORT_ASC;
27+
this.fieldKey = props.fieldKey;
1228
}
1329

1430
toRequestString(): string {
15-
const { dir, fieldKey } = this;
16-
return dir === '-' ? '-' + fieldKey : fieldKey;
31+
return `${this.dir}${this.fieldKey}`;
1732
}
1833
}

0 commit comments

Comments
 (0)