1+ import Badge from 'hew/Badge' ;
12import Button from 'hew/Button' ;
23import { DirectionType , Sort , validSort } from 'hew/DataGrid/DataGrid' ;
34import Dropdown , { MenuItem } from 'hew/Dropdown' ;
45import Icon from 'hew/Icon' ;
56import Select , { Option } from 'hew/Select' ;
67import { Loadable } from 'hew/utils/loadable' ;
8+ import { groupBy , mapValues } from 'lodash' ;
9+ import { Fragment , useMemo } from 'react' ;
710
11+ import { runColumns } from 'pages/FlatRuns/columns' ;
812import { V1ColumnType } from 'services/api-ts-sdk' ;
913import { ProjectColumn } from 'types' ;
14+ import { removeColumnTypePrefix } from 'utils/flatRun' ;
1015
1116import css from './MultiSortMenu.module.scss' ;
1217
@@ -25,6 +30,7 @@ interface MultiSortRowProps {
2530 onChange ?: ( sort : Sort ) => void ;
2631 onRemove ?: ( ) => void ;
2732 bannedSortColumns : Set < string > ;
33+ typeMap : Record < string , V1ColumnType [ ] > ;
2834}
2935interface DirectionOptionsProps {
3036 onChange ?: ( direction : DirectionType ) => void ;
@@ -36,6 +42,7 @@ interface ColumnOptionsProps {
3642 onChange ?: ( column : string ) => void ;
3743 value ?: string ;
3844 bannedSortColumns : Set < string > ;
45+ typeMap : Record < string , V1ColumnType [ ] > ;
3946}
4047
4148export const optionsByColumnType = {
@@ -153,6 +160,7 @@ const ColumnOptions: React.FC<ColumnOptionsProps> = ({
153160 columns,
154161 value,
155162 bannedSortColumns,
163+ typeMap,
156164} ) => (
157165 < Select
158166 autoFocus
@@ -161,10 +169,31 @@ const ColumnOptions: React.FC<ColumnOptionsProps> = ({
161169 loading = { Loadable . isNotLoaded ( columns ) }
162170 options = { Loadable . getOrElse ( [ ] , columns )
163171 . filter ( ( c ) => ! bannedSortColumns . has ( c . column ) )
164- . map ( ( c ) => ( {
165- label : c . displayName || c . column ,
166- value : c . column ,
167- } ) ) }
172+ . map ( ( c ) => {
173+ const badges = typeMap [ c . column ] . map ( ( type ) => {
174+ const copy =
175+ ( runColumns as readonly string [ ] ) . includes ( c . column ) &&
176+ type === V1ColumnType . UNSPECIFIED
177+ ? 'BOOLEAN'
178+ : removeColumnTypePrefix ( type ) ;
179+ return (
180+ < Fragment key = { type } >
181+ < Badge text = { copy } /> { ' ' }
182+ </ Fragment >
183+ ) ;
184+ } ) ;
185+ const title = c . displayName || c . column ;
186+ const label = (
187+ < >
188+ { title } { badges }
189+ </ >
190+ ) ;
191+ return {
192+ label,
193+ title,
194+ value : c . column ,
195+ } ;
196+ } ) }
168197 placeholder = "Select column"
169198 value = { value }
170199 width = "100%"
@@ -178,6 +207,7 @@ const MultiSortRow: React.FC<MultiSortRowProps> = ({
178207 onChange,
179208 onRemove,
180209 bannedSortColumns,
210+ typeMap,
181211} ) => {
182212 const valueType =
183213 Loadable . getOrElse ( [ ] , columns ) . find ( ( c ) => c . column === sort . column ) ?. type ||
@@ -188,6 +218,7 @@ const MultiSortRow: React.FC<MultiSortRowProps> = ({
188218 < ColumnOptions
189219 bannedSortColumns = { bannedSortColumns }
190220 columns = { columns }
221+ typeMap = { typeMap }
191222 value = { sort . column }
192223 onChange = { ( column ) => onChange ?.( { ...sort , column } ) }
193224 />
@@ -231,14 +262,35 @@ const MultiSort: React.FC<MultiSortProps> = ({ sorts, columns, onChange, bannedS
231262 window . document . body . dispatchEvent ( new Event ( 'mousedown' , { bubbles : true } ) ) ;
232263 onChange ?.( [ EMPTY_SORT ] ) ;
233264 } ;
265+ // replace duplicate columns with a single unspecified column for copy
266+ // reasons. maintain types so we can display in the select
267+ const [ mergedColumns , typeMap ] = useMemo ( ( ) => {
268+ const loadableTuple = columns . map ( ( c ) => {
269+ const columnGroups = groupBy ( c , 'column' ) ;
270+ const fixedColumns = Object . values ( columnGroups ) . flatMap ( ( group ) => {
271+ if ( group . length > 1 ) {
272+ return [
273+ {
274+ ...group [ 0 ] ,
275+ type : V1ColumnType . UNSPECIFIED ,
276+ } ,
277+ ] ;
278+ }
279+ return group ;
280+ } ) ;
281+ const typeMap = mapValues ( columnGroups , ( group ) => group . map ( ( column ) => column . type ) ) ;
282+ return [ fixedColumns , typeMap ] as const ;
283+ } ) ;
284+ return [ loadableTuple . map ( ( l ) => l [ 0 ] ) , loadableTuple . map ( ( l ) => l [ 1 ] ) . getOrElse ( { } ) ] ;
285+ } , [ columns ] ) ;
234286
235287 return (
236288 < div className = { css . base } data-test-component = "multiSort" >
237289 < div > { SORT_MENU_TITLE } </ div >
238290 < div className = { css . rows } data-test = "rows" >
239291 { sorts . map ( ( sort , idx ) => {
240292 const seenColumns = sorts . slice ( 0 , idx ) . map ( ( s ) => s . column ) ;
241- const columnOptions = Loadable . map ( columns , ( cols ) =>
293+ const columnOptions = mergedColumns . map ( ( cols ) =>
242294 cols . filter ( ( c ) => ! seenColumns . includes ( c . column ) ) ,
243295 ) ;
244296 return (
@@ -247,6 +299,7 @@ const MultiSort: React.FC<MultiSortProps> = ({ sorts, columns, onChange, bannedS
247299 columns = { columnOptions }
248300 key = { sort . column || idx }
249301 sort = { sort }
302+ typeMap = { typeMap }
250303 onChange = { makeOnRowChange ( idx ) }
251304 onRemove = { makeOnRowRemove ( idx ) }
252305 />
0 commit comments