From b83e978df8c9ffda3759378318267b8d0020653c Mon Sep 17 00:00:00 2001 From: Bill Wallace Date: Wed, 11 Mar 2026 14:28:20 -0400 Subject: [PATCH] fix: Modalities in study list should select starts with as primary (#5886) --- platform/ui/src/components/Select/Select.tsx | 39 +++++++++++++++++++- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/platform/ui/src/components/Select/Select.tsx b/platform/ui/src/components/Select/Select.tsx index c219d7fe3..310b39425 100644 --- a/platform/ui/src/components/Select/Select.tsx +++ b/platform/ui/src/components/Select/Select.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useState } from 'react'; import PropTypes from 'prop-types'; import classnames from 'classnames'; import ReactSelect, { components } from 'react-select'; @@ -6,6 +6,34 @@ import { Icons } from '@ohif/ui-next'; import './Select.css'; +/** + * Reorder options so items that START with the search text come first + * (shortest first), then items that only contain the text. This order is + * passed to react-select so the focused/highlighted option (e.g. on Enter) is correct. + */ +function orderOptionsByStartsWith(options, searchInput) { + if (!options?.length || !searchInput?.trim()) return options ?? []; + const input = searchInput.trim().toLowerCase(); + const startsWith = options.filter(opt => { + const label = (opt.label ?? opt.value ?? '').toString().toLowerCase(); + return label.startsWith(input); + }); + const containsOnly = options.filter(opt => { + const label = (opt.label ?? opt.value ?? '').toString().toLowerCase(); + return label.includes(input) && !label.startsWith(input); + }); + const rest = options.filter(opt => { + const label = (opt.label ?? opt.value ?? '').toString().toLowerCase(); + return !label.includes(input); + }); + startsWith.sort((a, b) => { + const lenA = (a.label ?? a.value ?? '').toString().length; + const lenB = (b.label ?? b.value ?? '').toString().length; + return lenA - lenB; + }); + return [...startsWith, ...containsOnly, ...rest]; +} + const MultiValue = props => { const values = props.selectProps.value; const lastValue = values[values.length - 1]; @@ -56,6 +84,11 @@ const Select = ({ components = {}, value = [], }) => { + const [filterInput, setFilterInput] = useState(''); + const orderedOptions = isSearchable + ? orderOptionsByStartsWith(options, filterInput) + : options; + const _noIconComponents = { DropdownIndicator: () => null, IndicatorSeparator: () => null, @@ -92,9 +125,11 @@ const Select = ({ hideSelectedOptions={hideSelectedOptions} components={_components} placeholder={placeholder} - options={options} + options={orderedOptions} blurInputOnSelect={true} menuPortalTarget={document.body} + onInputChange={isSearchable ? setFilterInput : undefined} + onMenuClose={isSearchable ? () => setFilterInput('') : undefined} styles={{ menuPortal: base => ({ ...base, zIndex: 9999 }), }}