Skip to content

Commit c87257a

Browse files
authored
Merge branch 'discvr-25.3' into 25.3_fb_newExport
2 parents 380dc9d + a4f691d commit c87257a

File tree

7 files changed

+173
-96
lines changed

7 files changed

+173
-96
lines changed

jbrowse/src/client/JBrowse/VariantSearch/components/FilterForm.tsx

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import React, { useState } from 'react';
22
import TextField from '@mui/material/TextField';
33
import Button from '@mui/material/Button';
44
import Select from '@mui/material/Select';
5+
import ReactSelect from 'react-select';
56
import AsyncSelect from 'react-select/async';
67
import MenuItem from '@mui/material/MenuItem';
78
import FormControl from '@mui/material/FormControl';
@@ -109,7 +110,7 @@ const FilterForm = (props: FilterFormProps ) => {
109110
updatedFilter.value = '';
110111
}
111112

112-
if (value === "in set" || filter.operator === "in set") {
113+
if (value === "equals one of" || filter.operator === "equals one of") {
113114
updatedFilter.value = '';
114115
}
115116
}
@@ -211,25 +212,33 @@ const FilterForm = (props: FilterFormProps ) => {
211212
<FormScroll>
212213
{filters.map((filter, index) => (
213214
<FilterRow key={index} >
214-
<FormControlMinWidth sx={ highlightedInputs[index]?.field ? highlightedSx : null }>
215-
<InputLabel id="field-label">Field</InputLabel>
216-
<Select
217-
labelId="field-label"
218-
label = 'Field'
219-
value={filter.field}
220-
onChange={(event) =>
221-
handleFilterChange(index, "field", event.target.value)
215+
<FormControlMinWidth sx={highlightedInputs[index]?.field ? highlightedSx : null}>
216+
<ReactSelect
217+
inputId={`field-select-${index}`}
218+
aria-labelledby={`field-label`}
219+
placeholder="Select field..."
220+
menuPortalTarget={document.body}
221+
menuPosition="fixed"
222+
styles={{
223+
menuPortal: (base) => ({ ...base, zIndex: 9999 }),
224+
}}
225+
options={fieldTypeInfo.map(field => ({
226+
value: field.name,
227+
label: field.label ?? field.name,
228+
}))}
229+
onChange={(selected) =>
230+
handleFilterChange(index, 'field', selected?.value ?? '')
222231
}
223-
>
224-
<MenuItem value="" style={{ display: 'none' }}>
225-
<em>None</em>
226-
</MenuItem>
227-
{fieldTypeInfo.map((field) => (
228-
<MenuItem key={field.name} value={field.name}>
229-
{field.label ?? field.name}
230-
</MenuItem>
231-
))}
232-
</Select>
232+
value={
233+
filter.field
234+
? {
235+
value: filter.field,
236+
label: fieldTypeInfo.find(f => f.name === filter.field)?.label ?? filter.field
237+
}
238+
: null
239+
}
240+
isClearable
241+
/>
233242
</FormControlMinWidth>
234243

235244
<FormControlMinWidth sx={ highlightedInputs[index]?.operator ? highlightedSx : null } >
@@ -259,7 +268,7 @@ const FilterForm = (props: FilterFormProps ) => {
259268
</Select>
260269
</FormControlMinWidth>
261270

262-
{filter.operator === "in set" ? (
271+
{filter.operator === "equals one of" ? (
263272
<FormControlMinWidth sx={ highlightedInputs[index]?.value ? highlightedSx : null } >
264273
<InputLabel id="value-select-label">Value</InputLabel>
265274
<Select
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import React from 'react';
2+
import { Button } from '@mui/material';
3+
import LinkIcon from '@mui/icons-material/Link';
4+
5+
export const ShareButton = () => {
6+
return (
7+
<Button
8+
startIcon={<LinkIcon />}
9+
size="small"
10+
color="primary"
11+
onClick={() => {
12+
navigator.clipboard.writeText(window.location.href)
13+
.then(() => {
14+
alert('URL copied to clipboard.');
15+
})
16+
.catch(err => {
17+
console.error('Failed to copy the URL: ', err);
18+
alert('Failed to copy the URL.');
19+
});
20+
}}
21+
>
22+
Share
23+
</Button>
24+
);
25+
};

jbrowse/src/client/JBrowse/VariantSearch/components/VariantTableWidget.tsx

Lines changed: 11 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,10 @@ import '../../jbrowse.css';
3939
import LoadingIndicator from './LoadingIndicator';
4040
import { BaseFeatureDataAdapter } from '@jbrowse/core/data_adapters/BaseAdapter';
4141
import { lastValueFrom } from 'rxjs';
42+
import { ShareButton } from './ShareButton';
4243

4344
const VariantTableWidget = observer(props => {
45+
const numberFormatter = new Intl.NumberFormat('en-US');
4446
const { assembly, trackId, parsedLocString, sessionId, session, pluginManager } = props;
4547
const { assemblyNames = [], assemblyManager } = session ?? {};
4648
const { view } = session ?? {};
@@ -228,25 +230,7 @@ const VariantTableWidget = observer(props => {
228230
Export CSV
229231
</Button>
230232

231-
<Button
232-
startIcon={<LinkIcon />}
233-
size="small"
234-
color="primary"
235-
onClick={() => {
236-
navigator.clipboard.writeText(window.location.href)
237-
.then(() => {
238-
// Popup message for successful copy
239-
alert('URL copied to clipboard.');
240-
})
241-
.catch(err => {
242-
// Error handling
243-
console.error('Failed to copy the URL: ', err);
244-
alert('Failed to copy the URL.');
245-
});
246-
}}
247-
>
248-
Share
249-
</Button>
233+
<ShareButton />
250234
</GridToolbarContainer>
251235
);
252236
}
@@ -500,6 +484,14 @@ const VariantTableWidget = observer(props => {
500484
setSortModel(newModel)
501485
handleQuery(filters, true, { page: 0, pageSize: pageSizeModel.pageSize }, newModel);
502486
}}
487+
localeText={{
488+
MuiTablePagination: {
489+
labelDisplayedRows: ({ from, to, count }) =>
490+
`${numberFormatter.format(from)}${numberFormatter.format(to)} of ${
491+
count !== -1 ? numberFormatter.format(count) : 'more than ' + numberFormatter.format(to)
492+
}`,
493+
},
494+
}}
503495
/>
504496
)
505497

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import React from 'react';
2+
import { Button } from '@mui/material';
3+
import LinkIcon from '@mui/icons-material/Link';
4+
5+
export const ShareButton = () => {
6+
return (
7+
<Button
8+
startIcon={<LinkIcon />}
9+
size="small"
10+
color="primary"
11+
onClick={() => {
12+
navigator.clipboard.writeText(window.location.href)
13+
.then(() => {
14+
alert('URL copied to clipboard.');
15+
})
16+
.catch(err => {
17+
console.error('Failed to copy the URL: ', err);
18+
alert('Failed to copy the URL.');
19+
});
20+
}}
21+
>
22+
Share
23+
</Button>
24+
);
25+
};

jbrowse/src/client/JBrowse/VariantTable/components/VariantTableWidget.tsx

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,29 @@ import React, { useEffect, useState } from 'react';
33
import { getConf } from '@jbrowse/core/configuration';
44
import { ParsedLocString, parseLocString } from '@jbrowse/core/util';
55
import { getAdapter } from '@jbrowse/core/data_adapters/dataAdapterCache';
6-
import { AppBar, Box, Button, Dialog, Grid, MenuItem, Paper, Toolbar, Typography } from '@mui/material';
6+
import {
7+
AppBar,
8+
Box,
9+
Button,
10+
Dialog,
11+
Grid,
12+
MenuItem,
13+
Paper,
14+
Toolbar,
15+
Typography,
16+
} from '@mui/material';
717
import ScopedCssBaseline from '@mui/material/ScopedCssBaseline';
818
import {
919
DataGrid,
1020
GridColDef,
1121
GridColumnVisibilityModel, GridFilterPanel,
1222
GridPaginationModel,
1323
GridRenderCellParams,
14-
GridToolbar
24+
GridToolbar,
25+
GridToolbarColumnsButton,
26+
GridToolbarFilterButton,
27+
GridToolbarDensitySelector,
28+
GridToolbarExport
1529
} from '@mui/x-data-grid';
1630
import MenuButton from './MenuButton';
1731
import '../../jbrowse.css';
@@ -27,6 +41,7 @@ import {
2741
passesSampleFilters
2842
} from '../../utils';
2943
import LoadingIndicator from './LoadingIndicator';
44+
import { ShareButton } from './ShareButton';
3045
import { NoAssemblyRegion } from '@jbrowse/core/util/types';
3146
import StandaloneSearchComponent from '../../Search/components/StandaloneSearchComponent';
3247
import { VcfFeature } from '@jbrowse/plugin-variants';
@@ -352,12 +367,24 @@ const VariantTableWidget = observer(props => {
352367
}
353368
}
354369

370+
const CustomToolbar = () => (
371+
<Box sx={{ display: 'flex', justifyContent: 'space-between', p: 1 }}>
372+
<Box>
373+
<GridToolbarColumnsButton />
374+
<GridToolbarFilterButton />
375+
<GridToolbarDensitySelector />
376+
<GridToolbarExport />
377+
<ShareButton />
378+
</Box>
379+
</Box>
380+
);
381+
355382
const gridElement = (
356383
// NOTE: the filterPanel/sx override is added to fix an issue where the grid column filter value input doesn't align with the field and operator inputs
357384
<DataGrid
358385
columns={[...gridColumns, actionsCol]}
359386
rows={features.map((rawFeature, id) => rawFeatureToRow(rawFeature, id, gridColumns, trackId))}
360-
slots={{ toolbar: GridToolbar, filterPanel: GridFilterPanel }}
387+
slots={{ toolbar: CustomToolbar, filterPanel: GridFilterPanel }}
361388
slotProps={{
362389
filterPanel: {
363390
filterFormProps: {
@@ -444,7 +471,7 @@ const VariantTableWidget = observer(props => {
444471
</Grid>
445472

446473
{supportsLuceneIndex ? <Grid key='luceneViewButton' item xs="auto">
447-
<Button hidden={!supportsLuceneIndex} style={{ marginTop:"8px"}} color="primary" variant="contained" onClick={() => handleMenu("luceneRedirect")}>Switch to Free-text Search</Button>
474+
<Button hidden={!supportsLuceneIndex} style={{ marginTop:"8px"}} color="primary" variant="contained" onClick={() => handleMenu("luceneRedirect")}>Switch to Full-text Search</Button>
448475
</Grid> : null}
449476
</Grid>
450477
</div>

jbrowse/src/client/JBrowse/utils.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,7 @@ export function serializeLocationToLuceneQuery(contig, start, end) {
330330
function generateLuceneString(field, operator, value) {
331331
let luceneQueryString = '';
332332

333-
if (field === 'variableSamples' && operator == "in set") {
333+
if (field === 'variableSamples' && operator == "equals one of") {
334334
return `variableSamples:~${value}~`;
335335
}
336336
let intValue = parseInt(value);
@@ -656,7 +656,7 @@ export function searchStringToInitialFilters(knownFieldNames: string[]) : Filter
656656

657657
export function getOperatorsForField(fieldObj: FieldModel): string[] {
658658
const stringOperators = ["equals", "does not equal", "contains", "does not contain", "starts with", "ends with", "is empty", "is not empty"];
659-
const variableSamplesType = ["in set", "variable in", "not variable in", "variable in all of", "variable in any of", "not variable in any of", "not variable in one of", "is empty", "is not empty"];
659+
const variableSamplesType = ["equals one of", "variable in", "not variable in", "variable in all of", "variable in any of", "not variable in any of", "not variable in one of", "is empty", "is not empty"];
660660
const numericOperators = ["=", "!=", ">", ">=", "<", "<="];
661661
const noneOperators = [];
662662

0 commit comments

Comments
 (0)