diff --git a/openbis_ng_ui/src/js/components/common/grid/GridController.js b/openbis_ng_ui/src/js/components/common/grid/GridController.js index 834cb7c654d9635748d228b610849810b0810fb4..d7adbb399edb98d3009cbaab94096161fa4e0554 100644 --- a/openbis_ng_ui/src/js/components/common/grid/GridController.js +++ b/openbis_ng_ui/src/js/components/common/grid/GridController.js @@ -11,13 +11,24 @@ export default class GridController { init(context) { const props = context.getProps() - const columns = props.columns.map(column => ({ - ...column, - label: column.label || _.upperFirst(column.field), - render: column.render || (row => this._getValue(row, column.field)), - sort: column.sort === undefined ? true : column.sort, - visible: true - })) + const columns = [] + let initialSort = null + let initialSortDirection = null + + props.columns.forEach(column => { + if (column.sort) { + initialSort = column.field + initialSortDirection = column.sort + } + columns.push({ + ...column, + field: column.field, + label: column.label || _.upperFirst(column.field), + render: column.render || (row => this._getValue(row, column.field)), + sortable: column.sortable === undefined ? true : column.sortable, + visible: true + }) + }) context.initState({ loaded: false, @@ -31,8 +42,8 @@ export default class GridController { currentRows: [], selectedRowId: null, selectedRow: null, - sort: null, - sortDirection: 'asc' + sort: initialSort, + sortDirection: initialSortDirection }) this.context = context @@ -282,14 +293,23 @@ export default class GridController { } handleSortChange(column) { - if (!column.sort) { + if (!column.sortable) { return } + this.context - .setState(prevState => ({ - sort: column.field, - sortDirection: prevState.sortDirection === 'asc' ? 'desc' : 'asc' - })) + .setState(state => { + if (column.field === state.sort) { + return { + sortDirection: state.sortDirection === 'asc' ? 'desc' : 'asc' + } + } else { + return { + sort: column.field, + sortDirection: 'asc' + } + } + }) .then(() => { this._saveSettings() this._recalculateCurrentRows() diff --git a/openbis_ng_ui/src/js/components/common/grid/GridHeaderLabel.jsx b/openbis_ng_ui/src/js/components/common/grid/GridHeaderLabel.jsx index 96e1daab89afc676e799e92b0d5d24aeb739ceaa..98f15b42ba69387eff4e8f2666c9e735ec4b204c 100644 --- a/openbis_ng_ui/src/js/components/common/grid/GridHeaderLabel.jsx +++ b/openbis_ng_ui/src/js/components/common/grid/GridHeaderLabel.jsx @@ -30,12 +30,13 @@ class GridHeaderLabel extends React.PureComponent { const { column, sort, sortDirection, classes } = this.props if (column.visible) { - if (column.sort) { + if (column.sortable) { + const active = sort === column.field return ( <TableCell classes={{ root: classes.cell }}> <TableSortLabel - active={sort === column.field} - direction={sortDirection} + active={active} + direction={active ? sortDirection : 'asc'} onClick={this.handleClick} > {column.label} diff --git a/openbis_ng_ui/src/js/components/types/form/VocabularyForm.jsx b/openbis_ng_ui/src/js/components/types/form/VocabularyForm.jsx index 3025a88243d1a7726045a810bac9e84e4f76c85c..d9843eb56db7416d92503130bd95b8a1a5de8017 100644 --- a/openbis_ng_ui/src/js/components/types/form/VocabularyForm.jsx +++ b/openbis_ng_ui/src/js/components/types/form/VocabularyForm.jsx @@ -78,7 +78,8 @@ class VocabularyForm extends React.PureComponent { columns={[ { field: 'code.value', - label: 'Code' + label: 'Code', + sort: 'asc' }, { field: 'label.value', diff --git a/openbis_ng_ui/src/js/components/types/search/TypeSearch.jsx b/openbis_ng_ui/src/js/components/types/search/TypeSearch.jsx index 1545d3a322e18a81c169dc60fa42704fbd1ce9f4..3bf79c0ae8bdf18e6ab812e440ca61ece014b7f4 100644 --- a/openbis_ng_ui/src/js/components/types/search/TypeSearch.jsx +++ b/openbis_ng_ui/src/js/components/types/search/TypeSearch.jsx @@ -143,10 +143,6 @@ class TypeSearch extends React.Component { <Grid id={ids.TYPES_GRID_ID} columns={[ - { - field: 'permId.entityKind', - label: 'Kind' - }, { field: 'code', render: row => ( @@ -157,7 +153,12 @@ class TypeSearch extends React.Component { > {row.code} </Link> - ) + ), + sort: 'asc' + }, + { + field: 'permId.entityKind', + label: 'Kind' }, { field: 'description' diff --git a/openbis_ng_ui/src/js/components/users/search/UserSearch.jsx b/openbis_ng_ui/src/js/components/users/search/UserSearch.jsx index 3b7b6a5ab254cfe6f60c3761cc6f4bc8a78b150a..b5c63c61870a332f1e2fef4c260fa760a4699fb7 100644 --- a/openbis_ng_ui/src/js/components/users/search/UserSearch.jsx +++ b/openbis_ng_ui/src/js/components/users/search/UserSearch.jsx @@ -64,7 +64,8 @@ class UserSearch extends React.Component { id={ids.USERS_GRID_ID} columns={[ { - field: 'userId' + field: 'userId', + sort: 'asc' }, { field: 'firstName' diff --git a/openbis_ng_ui/srcTest/js/components/types/form/VocabularyFormComponent.test.js b/openbis_ng_ui/srcTest/js/components/types/form/VocabularyFormComponent.test.js index 258eb0e59a32e69cc9a0738a0cbcd82abf247ec0..3a838a87ea8562d994247aa0c9b513040ba313fd 100644 --- a/openbis_ng_ui/srcTest/js/components/types/form/VocabularyFormComponent.test.js +++ b/openbis_ng_ui/srcTest/js/components/types/form/VocabularyFormComponent.test.js @@ -27,6 +27,7 @@ beforeEach(() => { describe('VocabularyFormComponent', () => { test('load new', testLoadNew) test('load existing', testLoadExisting) + test('sort', testSort) test('select term', testSelectTerm) test('add term', testAddTerm) test('remove term', testRemoveTerm) @@ -94,7 +95,7 @@ async function testLoadExisting() { filter: { value: null }, - sort: null + sort: 'asc' }, { field: 'label.value', @@ -213,6 +214,76 @@ async function testLoadExisting() { }) } +async function testSort() { + const form = await mountNew() + + const labels = [ + 'Term 1', + 'term 11', + 'Term 2', + 'TERM A', + 'term B', + 'Term A1', + 'tErM A11', + 'term A2' + ] + + for (let i = 0; i < labels.length; i++) { + form.getButtons().getAddTerm().click() + await form.update() + form.getParameters().getTerm().getLabel().change(labels[i]) + await form.update() + } + + form.getGrid().getColumns()[1].getLabel().click() + await form.update() + + form.expectJSON({ + grid: { + columns: [ + { field: 'code.value', sort: null }, + { field: 'label.value', sort: 'asc' }, + { field: 'description.value', sort: null }, + { field: 'official.value', sort: null } + ], + rows: [ + { values: { 'label.value': 'Term 1' } }, + { values: { 'label.value': 'Term 2' } }, + { values: { 'label.value': 'term 11' } }, + { values: { 'label.value': 'TERM A' } }, + { values: { 'label.value': 'Term A1' } }, + { values: { 'label.value': 'term A2' } }, + { values: { 'label.value': 'tErM A11' } }, + { values: { 'label.value': 'term B' } } + ] + } + }) + + form.getGrid().getColumns()[1].getLabel().click() + await form.update() + + form.expectJSON({ + grid: { + columns: [ + { field: 'code.value', sort: null }, + { field: 'label.value', sort: 'desc' }, + { field: 'description.value', sort: null }, + { field: 'official.value', sort: null } + ], + rows: [ + { values: { 'label.value': 'term B' } }, + { values: { 'label.value': 'tErM A11' } }, + { values: { 'label.value': 'term A2' } }, + { values: { 'label.value': 'Term A1' } }, + { values: { 'label.value': 'TERM A' } }, + { values: { 'label.value': 'term 11' } }, + { values: { 'label.value': 'Term 2' } }, + { values: { 'label.value': 'Term 1' } } + ] + } + }) +} + async function testSelectTerm() { const form = await mountExisting() @@ -286,12 +357,22 @@ async function testAddTerm() { form.expectJSON({ grid: { - rows: fixture.TEST_VOCABULARY_DTO.terms.map(term => ({ + columns: [ + { field: 'code.value', sort: 'asc' }, + { field: 'label.value', sort: null }, + { field: 'description.value', sort: null }, + { field: 'official.value', sort: null } + ], + rows: [ + fixture.TEST_TERM_1_DTO, + fixture.TEST_TERM_2_DTO, + fixture.TEST_TERM_3_DTO, + fixture.TEST_TERM_4_DTO, + fixture.TEST_TERM_5_DTO, + fixture.TEST_TERM_6_DTO + ].map(term => ({ values: { - 'code.value': term.getCode(), - 'label.value': term.getLabel(), - 'description.value': term.getDescription(), - 'official.value': String(term.isOfficial()) + 'code.value': term.getCode() }, selected: false })), @@ -304,6 +385,9 @@ async function testAddTerm() { } }) + form.getGrid().getColumns()[0].getLabel().click() + await form.update() + form.getGrid().getPaging().getPageSize().change(5) await form.update() @@ -315,9 +399,15 @@ async function testAddTerm() { form.expectJSON({ grid: { + columns: [ + { field: 'code.value', sort: 'desc' }, + { field: 'label.value', sort: null }, + { field: 'description.value', sort: null }, + { field: 'official.value', sort: null } + ], rows: [ { - values: { 'code.value': fixture.TEST_TERM_6_DTO.getCode() }, + values: { 'code.value': fixture.TEST_TERM_1_DTO.getCode() }, selected: false }, { @@ -497,16 +587,17 @@ async function testChangeTerm() { form.getGrid().getRows()[1].click() await form.update() - form - .getGrid() - .getRows()[1] - .expectJSON({ - values: { - 'label.value': fixture.TEST_TERM_2_DTO.getLabel() - } - }) - form.expectJSON({ + grid: { + rows: [ + { values: { 'label.value': fixture.TEST_TERM_1_DTO.getLabel() } }, + { values: { 'label.value': fixture.TEST_TERM_2_DTO.getLabel() } }, + { values: { 'label.value': fixture.TEST_TERM_3_DTO.getLabel() } }, + { values: { 'label.value': fixture.TEST_TERM_4_DTO.getLabel() } }, + { values: { 'label.value': fixture.TEST_TERM_5_DTO.getLabel() } }, + { values: { 'label.value': fixture.TEST_TERM_6_DTO.getLabel() } } + ] + }, parameters: { term: { title: 'Term', @@ -526,16 +617,17 @@ async function testChangeTerm() { form.getParameters().getTerm().getLabel().change('New Label') await form.update() - form - .getGrid() - .getRows()[1] - .expectJSON({ - values: { - 'label.value': 'New Label' - } - }) - form.expectJSON({ + grid: { + rows: [ + { values: { 'label.value': fixture.TEST_TERM_1_DTO.getLabel() } }, + { values: { 'label.value': 'New Label' } }, + { values: { 'label.value': fixture.TEST_TERM_3_DTO.getLabel() } }, + { values: { 'label.value': fixture.TEST_TERM_4_DTO.getLabel() } }, + { values: { 'label.value': fixture.TEST_TERM_5_DTO.getLabel() } }, + { values: { 'label.value': fixture.TEST_TERM_6_DTO.getLabel() } } + ] + }, parameters: { term: { title: 'Term', @@ -619,20 +711,14 @@ async function testValidateTerm() { form.getGrid().getPaging().getPageSize().change(5) await form.update() + form.getGrid().getPaging().getNextPage().click() + await form.update() + form.expectJSON({ grid: { - rows: [ - fixture.TEST_TERM_1_DTO, - fixture.TEST_TERM_2_DTO, - fixture.TEST_TERM_3_DTO, - fixture.TEST_TERM_4_DTO, - fixture.TEST_TERM_5_DTO - ].map(term => ({ + rows: [fixture.TEST_TERM_5_DTO, fixture.TEST_TERM_6_DTO].map(term => ({ values: { - 'code.value': term.getCode(), - 'label.value': term.getLabel(), - 'description.value': term.getDescription(), - 'official.value': String(term.isOfficial()) + 'code.value': term.getCode() }, selected: false })), @@ -640,7 +726,7 @@ async function testValidateTerm() { pageSize: { value: 5 }, - range: '1-5 of 7' + range: '6-7 of 7' } }, parameters: { @@ -666,15 +752,6 @@ async function testValidateTerm() { form.expectJSON({ grid: { rows: [ - { - values: { - 'code.value': fixture.TEST_TERM_6_DTO.getCode(), - 'label.value': fixture.TEST_TERM_6_DTO.getLabel(), - 'description.value': fixture.TEST_TERM_6_DTO.getDescription(), - 'official.value': String(fixture.TEST_TERM_6_DTO.isOfficial()) - }, - selected: false - }, { values: { 'code.value': null, @@ -683,13 +760,17 @@ async function testValidateTerm() { 'official.value': String(true) }, selected: true - } + }, + { values: { 'code.value': fixture.TEST_TERM_1_DTO.getCode() } }, + { values: { 'code.value': fixture.TEST_TERM_2_DTO.getCode() } }, + { values: { 'code.value': fixture.TEST_TERM_3_DTO.getCode() } }, + { values: { 'code.value': fixture.TEST_TERM_4_DTO.getCode() } } ], paging: { pageSize: { value: 5 }, - range: '6-7 of 7' + range: '1-5 of 7' } }, parameters: {