Skip to content

Commit

Permalink
Closes geosolutions-it#3534 switch content tab when 0 results (maps/d…
Browse files Browse the repository at this point in the history
…ashboards) (geosolutions-it#3733)

(cherry picked from commit 5c8f7e9)
  • Loading branch information
kappu72 committed May 10, 2019
1 parent 25df74b commit fc4a2b9
Show file tree
Hide file tree
Showing 9 changed files with 227 additions and 22 deletions.
25 changes: 25 additions & 0 deletions web/client/actions/__tests__/contenttabs-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright 2018, GeoSolutions Sas.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/

const expect = require('expect');

const {
onTabSelected,
ON_TAB_SELECTED
} = require('../contenttabs');

describe('Test contenttabs actions', () => {

it('Test onTabSelected action creator', () => {
const id = 'layer_001';
const retval = onTabSelected(id);
expect(retval).toExist();
expect(retval.id).toBe(id);
expect(retval.type).toBe(ON_TAB_SELECTED);
});
});
26 changes: 26 additions & 0 deletions web/client/actions/contenttabs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright 2019, GeoSolutions Sas.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/

const ON_TAB_SELECTED = 'CONTENT_TABS:ON_TAB_SELECTED';

/**
* Select Tab
* @memberof actions.contenttabs
* @param {string} id tab id
*
* @return {object} of type `ON_TAB_SELECTED` with tab id
*/

const onTabSelected = (id) => {
return {
type: ON_TAB_SELECTED,
id
};
};

module.exports = {onTabSelected, ON_TAB_SELECTED};
30 changes: 30 additions & 0 deletions web/client/epics/__tests__/contenttabs-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright 2019, GeoSolutions Sas.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/

var expect = require('expect');
const {testEpic} = require('./epicTestUtils');

const {MAPS_LOAD_MAP, MAPS_LIST_LOADED} = require("../../actions/maps");
const {DASHBOARDS_LIST_LOADED} = require("../../actions/dashboards");
const {updateMapsDashboardTabs} = require("../contenttabs");

describe('Test Maps Dashboard Content Tabs', () => {
it('test updateMapsDashboardTabs flow ', done => {
const act = [
{type: MAPS_LOAD_MAP},
{type: MAPS_LIST_LOADED, maps: {totalCount: 0}},
{type: DASHBOARDS_LIST_LOADED, totalCount: 1}
];
testEpic(updateMapsDashboardTabs, 1, act, (res) => {
const action = res[0];
expect(action).toExist();
expect(action.id).toBe("dashboards");
done();
}, {contenttabs: {selected: "maps"}});
});
});
39 changes: 39 additions & 0 deletions web/client/epics/contenttabs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright 2019, GeoSolutions Sas.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/

const Rx = require('rxjs');
const {findKey} = require('lodash');
const {MAPS_LOAD_MAP, MAPS_LIST_LOADED} = require("../actions/maps");
const {
DASHBOARDS_LIST_LOADED
} = require('../actions/dashboards');
const {onTabSelected} = require("../actions/contenttabs");
/**
* Update Maps and Dashboards counts to select contenttabs each tab has to have a key in its ContentTab configuration
* @param {object} action
*/
const updateMapsDashboardTabs = (action$, {getState = () => {}}) =>
action$.ofType(MAPS_LOAD_MAP)
.switchMap(() => {
return Rx.Observable.forkJoin(action$.ofType(MAPS_LIST_LOADED).take(1), action$.ofType(DASHBOARDS_LIST_LOADED).take(1))
.switchMap((r) => {
const results = {maps: r[0].maps, dashboards: r[1] };
const {contenttabs = {}} = getState() || {};
const {selected} = contenttabs;
if (results[selected] && results[selected].totalCount === 0) {
const id = findKey(results, ({totalCount}) => totalCount > 0);
if (id) {
return Rx.Observable.of(onTabSelected(id));
}
}
return Rx.Observable.empty();
});
});


module.exports = {updateMapsDashboardTabs};
46 changes: 39 additions & 7 deletions web/client/plugins/ContentTabs.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,37 @@ const { Row, Col, Grid, Nav, NavItem} = require('react-bootstrap');
const ToolsContainer = require('./containers/ToolsContainer');
const Message = require('../components/I18N/Message');

const {withState} = require('recompose');
const {connect} = require('react-redux');
const assign = require('object-assign');
const {createSelector} = require('reselect');
const {onTabSelected} = require('../actions/contenttabs');

const selectedSelector = createSelector(
state => state && state.contenttabs && state.contenttabs.selected,
selected => ({ selected })
);

const DefaultTitle = ({ item = {}, index }) => <span>{ item.title || `Tab ${index}` }</span>;

/**
* @name ContentTabs
* @memberof plugins
* @class
* @classdesc
* ContentTabs plugin is used in home page allowing to switch between contained plugins (i.e. Maps and Dashboards plugins).
* <br/>Each contained plugin has to have the contenttabs configuration property in its plugin configuration.
* The key property is mandatory following and position property is used to order give tabs order.
* An example of the contenttabs config in Maps plugin
* @example
* ContentTabs: {
* name: 'maps',
* key: 'maps',
* TitleComponent:
* connect(mapsCountSelector)(({ count = "" }) => <Message msgId="resources.maps.title" msgParams={{ count: count + "" }} />),
* position: 1,
* tool: true
* }
*/
class ContentTabs extends React.Component {
static propTypes = {
selected: PropTypes.number,
Expand All @@ -22,6 +50,7 @@ class ContentTabs extends React.Component {
items: PropTypes.array,
id: PropTypes.string,
onSelect: PropTypes.func

};
static defaultProps = {
selected: 0,
Expand All @@ -44,21 +73,21 @@ class ContentTabs extends React.Component {
container={(props) => <div {...props}>
<div style={{marginTop: "10px"}}>
<Nav bsStyle="tabs" activeKey="1" onSelect={k => this.props.onSelect(k)}>
{this.props.items.map(
{[...this.props.items].sort((a, b) => a.position - b.position).map(
({ TitleComponent = DefaultTitle, ...item }, idx) =>
(<NavItem
active={idx === this.props.selected}
active={(item.key || idx) === this.props.selected}
eventKey={item.key || idx} >
<TitleComponent index={idx} item={item} />
</NavItem>))}
</NavItem>))}
</Nav>
</div>
{props.children}
</div>}
toolStyle="primary"
stateSelector="contentTabs"
activeStyle="default"
tools={[...this.props.items].sort((a, b) => a.position - b.position).filter( (e, i) => i === this.props.selected)}
tools={[...this.props.items].sort((a, b) => a.position - b.position).filter( ({key}, i) => (key || i) === this.props.selected)}
panels={[]}
/></Col>
</Row>
Expand All @@ -69,13 +98,16 @@ class ContentTabs extends React.Component {
}

module.exports = {
ContentTabsPlugin: assign(withState('selected', 'onSelect', 0)(ContentTabs), {
ContentTabsPlugin: assign(connect(selectedSelector, {
onSelect: onTabSelected
})(ContentTabs), {
NavMenu: {
position: 2,
label: <Message msgId="resources.contents.title" />,
linkId: '#content-tabs',
glyph: 'dashboard'
}
}),
reducers: {}
reducers: {contenttabs: require('../reducers/contenttabs')},
epics: require('../epics/contenttabs')
};
14 changes: 9 additions & 5 deletions web/client/plugins/Dashboards.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ const DashboardGrid = require('./dashboard/DashboardsGrid');
const PaginationToolbar = require('./dashboard/PaginationToolbar');
const EmptyDashboardsView = require('./dashboard/EmptyDashboardsView');

const dashboardsCountSelector = createSelector(
totalCountSelector,
count => ({ count })
);


class Dashboards extends React.Component {
static propTypes = {
mapType: PropTypes.string,
Expand Down Expand Up @@ -110,12 +116,10 @@ module.exports = {
glyph: 'dashboard'
},
ContentTabs: {
name: 'maps',
name: 'dashboards',
key: 'dashboards',
TitleComponent:
connect(createSelector(
totalCountSelector,
count => ({count})
))(({ count = ""}) => <Message msgId="resources.dashboards.title" msgParams={{ count: count + "" }} />),
connect(dashboardsCountSelector)(({ count = ""}) => <Message msgId="resources.dashboards.title" msgParams={{ count: count + "" }} />),
position: 2,
tool: true,
priority: 1
Expand Down
17 changes: 7 additions & 10 deletions web/client/plugins/Maps.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ const MetadataModal = require('./maps/MetadataModal');

const {loadMaps, setShowMapDetails} = require('../actions/maps');

const mapsCountSelector = createSelector(
totalCountSelector,
count => ({ count })
);

const PaginationToolbar = connect((state) => {
if (!state.maps ) {
return {};
Expand Down Expand Up @@ -89,12 +94,6 @@ class Maps extends React.Component {
maps: []
};

componentDidMount() {
// if there is a change in the search text it uses that before the initialMapFilter
this.props.loadMaps(ConfigUtils.getDefaults().geoStoreUrl, this.props.searchText || ConfigUtils.getDefaults().initialMapFilter || "*", this.props.mapsOptions);
this.props.setShowMapDetails(this.props.showMapDetails);
}

render() {
return (<MapsGrid
maps={this.props.maps}
Expand Down Expand Up @@ -134,11 +133,9 @@ module.exports = {
},
ContentTabs: {
name: 'maps',
key: 'maps',
TitleComponent:
connect(createSelector(
totalCountSelector,
count => ({ count })
))(({ count = "" }) => <Message msgId="resources.maps.title" msgParams={{ count: count + "" }} />),
connect(mapsCountSelector)(({ count = "" }) => <Message msgId="resources.maps.title" msgParams={{ count: count + "" }} />),
position: 1,
tool: true,
priority: 1
Expand Down
29 changes: 29 additions & 0 deletions web/client/reducers/__tests__/contenttabs-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright 2019, GeoSolutions Sas.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/

const expect = require('expect');

const {
onTabSelected
} = require('../../actions/contenttabs');

const contenttabs = require('../contenttabs');

describe('Test contenttabs reducer', () => {

it('select correct tab', () => {
const id = 'dashboard';
const state = contenttabs({selected: "maps"}, onTabSelected(id));
expect(state).toEqual(
{
selected: id
}
);
});

});
23 changes: 23 additions & 0 deletions web/client/reducers/contenttabs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* Copyright 2019, GeoSolutions Sas.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/

const {
ON_TAB_SELECTED
} = require('../actions/contenttabs');

function contenttabs(state = {selected: "maps"}, action) {
switch (action.type) {
case ON_TAB_SELECTED: {
return {selected: action.id || "maps"};
}
default:
return state;
}
}

module.exports = contenttabs;

0 comments on commit fc4a2b9

Please sign in to comment.