-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
DR-3164: Handle null values in Free Text Tabular data filtering (#1523)
* Handle null values in freetext filtering * Run lint * Convert FreetextFilter to Typescript format format filter * Create Component test for FreetextFilter Update test format test Update FreetextFilter.test.tsx
- Loading branch information
Showing
6 changed files
with
230 additions
and
160 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
149 changes: 0 additions & 149 deletions
149
src/components/dataset/data/sidebar/filter/FreetextFilter.jsx
This file was deleted.
Oops, something went wrong.
69 changes: 69 additions & 0 deletions
69
src/components/dataset/data/sidebar/filter/FreetextFilter.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
import { Provider } from 'react-redux'; | ||
import { mount } from 'cypress/react'; | ||
import { Router } from 'react-router-dom'; | ||
import { ThemeProvider } from '@mui/styles'; | ||
import { routerMiddleware } from 'connected-react-router'; | ||
import createMockStore, { MockStoreEnhanced } from 'redux-mock-store'; | ||
import React from 'react'; | ||
import globalTheme from '../../../../../modules/theme'; | ||
import history from '../../../../../modules/hist'; | ||
import FreetextFilter from './FreetextFilter'; | ||
|
||
function createState(filterMap: any, values: any): any { | ||
return { | ||
datasets: { | ||
dataset: { | ||
description: 'A dataset description', | ||
phsId: '12345', | ||
}, | ||
}, | ||
column: { | ||
name: 'column1', | ||
label: 'column1', | ||
}, | ||
filterMap, | ||
values, | ||
}; | ||
} | ||
|
||
let store: MockStoreEnhanced<unknown, unknown>; | ||
|
||
function mountComponent(state: any) { | ||
const mockStore = createMockStore([routerMiddleware(history)]); | ||
store = mockStore(state); | ||
mount( | ||
<Provider store={store}> | ||
<Router history={history}> | ||
<ThemeProvider theme={globalTheme}> | ||
<FreetextFilter | ||
classes={{ listItem: 'listItemClass', chip: 'chipClass' }} | ||
column={state.column} | ||
filterMap={state.filterMap} | ||
handleChange={() => null} | ||
handleFilters={() => null} | ||
toggleExclude={() => null} | ||
values={state.values} | ||
/> | ||
</ThemeProvider> | ||
</Router> | ||
</Provider>, | ||
); | ||
} | ||
|
||
describe('Free text list that includes null value', () => { | ||
beforeEach(() => { | ||
const filterMap = { | ||
value: [null, '1:100891126:A:G'], | ||
type: 'value', | ||
exclude: false, | ||
}; | ||
const values = [{ value: null }, { value: '1:100891126:A:G' }, { value: '2:100891126:A:G' }]; | ||
const initialState = createState(filterMap, values); | ||
mountComponent(initialState); | ||
}); | ||
it('Load Free text dropdown', () => { | ||
cy.get('[id="autocomplete-column1"]').should('exist').click(); | ||
cy.get('#autocomplete-column1-option-0').should('contain.text', '(empty)'); | ||
cy.get('#autocomplete-column1-option-1').should('contain.text', '1:100891126:A:G'); | ||
}); | ||
}); |
145 changes: 145 additions & 0 deletions
145
src/components/dataset/data/sidebar/filter/FreetextFilter.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
import React, { useState } from 'react'; | ||
import _ from 'lodash'; | ||
import { connect } from 'react-redux'; | ||
import TextField from '@mui/material/TextField'; | ||
import Chip from '@mui/material/Chip'; | ||
import Autocomplete from '@mui/material/Autocomplete'; | ||
import { WithStyles, withStyles } from '@mui/styles'; | ||
import { FormControlLabel, Checkbox, Typography, CustomTheme } from '@mui/material'; | ||
import { TdrState } from 'reducers'; | ||
|
||
import { TableColumnType } from 'src/reducers/query'; | ||
|
||
const styles = (theme: CustomTheme) => ({ | ||
listItem: { | ||
margin: `${theme.spacing(0.5)} 0px`, | ||
}, | ||
chip: { | ||
backgroundColor: theme.palette.common.white, | ||
borderRadius: theme.shape.borderRadius, | ||
}, | ||
}); | ||
|
||
interface FreetextFilterProps extends WithStyles<typeof styles> { | ||
classes: any; | ||
column: TableColumnType; | ||
filterMap: any; | ||
handleChange: (value: any) => void; | ||
handleFilters: () => void; | ||
toggleExclude: (boxIsChecked: boolean) => void; | ||
values: any; | ||
} | ||
|
||
const FreetextFilter = withStyles(styles)( | ||
({ | ||
classes, | ||
column, | ||
filterMap, | ||
handleChange, | ||
handleFilters, | ||
toggleExclude, | ||
values, | ||
}: FreetextFilterProps) => { | ||
const [inputValue, setInputValue] = useState(''); | ||
|
||
const onInputChange = (event: React.ChangeEvent<HTMLInputElement>) => { | ||
const { value } = event.target; | ||
setInputValue(value); | ||
}; | ||
|
||
const onChange = (_event: any, value: any) => { | ||
setInputValue(''); | ||
if (handleChange) { | ||
handleChange(value); | ||
} | ||
}; | ||
|
||
const handleReturn = (event: React.KeyboardEvent<HTMLInputElement>) => { | ||
if (event.key === 'Enter' && handleFilters) { | ||
handleFilters(); | ||
} | ||
}; | ||
|
||
const onPaste = (event: React.ClipboardEvent<HTMLInputElement>) => { | ||
event.preventDefault(); | ||
const text = event.clipboardData.getData('text'); | ||
const selections = text.split(/[ ,\r\n]+/); | ||
const nonEmpty = selections.filter((s: string) => s !== ''); | ||
const updatedValueArray = _.get(filterMap, 'value', []).concat(nonEmpty); | ||
if (handleChange) { | ||
handleChange(updatedValueArray); | ||
} | ||
}; | ||
|
||
const deleteChip = (option: any) => { | ||
const selected = _.filter(filterMap.value, (v) => v !== option); | ||
handleChange(selected); | ||
}; | ||
|
||
const value = _.get(filterMap, 'value', []); | ||
const valueList = values?.map((val: any) => val.value) ?? []; | ||
|
||
return ( | ||
<div> | ||
<Autocomplete | ||
multiple | ||
id={`autocomplete-${column.name}`} | ||
options={valueList} | ||
getOptionLabel={(option) => option ?? '(empty)'} | ||
// this means the user's choice does not have to match the provided options | ||
freeSolo={true} | ||
style={{ width: '100%' }} | ||
renderInput={(params) => ( | ||
<TextField | ||
{...params} | ||
fullWidth | ||
variant="outlined" | ||
margin="dense" | ||
onChange={onInputChange} | ||
/> | ||
)} | ||
// tags are rendered manually in list under autocomplete box | ||
renderTags={() => null} | ||
inputValue={inputValue} | ||
onKeyPress={handleReturn} | ||
onPaste={onPaste} | ||
onChange={onChange} | ||
value={value} | ||
forcePopupIcon | ||
disableClearable | ||
/> | ||
{value.map((option: any, index: any) => ( | ||
<div key={index} className={classes.listItem}> | ||
<Chip | ||
label={option ?? '(empty)'} | ||
onDelete={() => deleteChip(option)} | ||
variant="outlined" | ||
className={classes.chip} | ||
/> | ||
</div> | ||
))} | ||
{!_.isEmpty(value) && ( | ||
<FormControlLabel | ||
control={ | ||
<Checkbox | ||
size="small" | ||
checked={filterMap.exclude} | ||
onChange={(event) => toggleExclude(event.target.checked)} | ||
/> | ||
} | ||
label={<Typography variant="body2">Exclude all</Typography>} | ||
data-cy={`exclude-${column.name}`} | ||
/> | ||
)} | ||
</div> | ||
); | ||
}, | ||
); | ||
|
||
function mapStateToProps(state: TdrState) { | ||
return { | ||
dataset: state.datasets.dataset, | ||
}; | ||
} | ||
|
||
export default connect(mapStateToProps)(FreetextFilter); |
Oops, something went wrong.