- } noCaret id="dropdown-no-caret">
+ {canEdit ? (} noCaret id="dropdown-no-caret">
-
+ ) : null}
}
>
diff --git a/web/client/components/widgets/widget/__tests__/ChartWidget-test.jsx b/web/client/components/widgets/widget/__tests__/ChartWidget-test.jsx
index d3463108e1..011bc1fca3 100644
--- a/web/client/components/widgets/widget/__tests__/ChartWidget-test.jsx
+++ b/web/client/components/widgets/widget/__tests__/ChartWidget-test.jsx
@@ -25,8 +25,16 @@ describe('ChartWidget component', () => {
ReactDOM.render(, document.getElementById("container"));
const container = document.getElementById('container');
const el = container.querySelector('.mapstore-widget-card');
+ expect(container.querySelector('.glyphicon-pencil')).toExist();
+ expect(container.querySelector('.glyphicon-trash')).toExist();
expect(el).toExist();
});
+ it('view only mode', () => {
+ ReactDOM.render(, document.getElementById("container"));
+ const container = document.getElementById('container');
+ expect(container.querySelector('.glyphicon-pencil')).toNotExist();
+ expect(container.querySelector('.glyphicon-trash')).toNotExist();
+ });
it('Test ChartWidget onEdit callback', () => {
const actions = {
onEdit: () => {}
diff --git a/web/client/components/widgets/widget/__tests__/CounterWidget-test.jsx b/web/client/components/widgets/widget/__tests__/CounterWidget-test.jsx
index 1526d7c68a..f21635fb64 100644
--- a/web/client/components/widgets/widget/__tests__/CounterWidget-test.jsx
+++ b/web/client/components/widgets/widget/__tests__/CounterWidget-test.jsx
@@ -25,8 +25,16 @@ describe('CounterWidget component', () => {
ReactDOM.render(, document.getElementById("container"));
const container = document.getElementById('container');
const el = container.querySelector('.mapstore-widget-card');
+ expect(container.querySelector('.glyphicon-pencil')).toExist();
+ expect(container.querySelector('.glyphicon-trash')).toExist();
expect(el).toExist();
});
+ it('view only mode', () => {
+ ReactDOM.render(, document.getElementById("container"));
+ const container = document.getElementById('container');
+ expect(container.querySelector('.glyphicon-pencil')).toNotExist();
+ expect(container.querySelector('.glyphicon-trash')).toNotExist();
+ });
it('Test CounterWidget onEdit callback', () => {
const actions = {
onEdit: () => {}
diff --git a/web/client/components/widgets/widget/__tests__/LegendWidget-test.jsx b/web/client/components/widgets/widget/__tests__/LegendWidget-test.jsx
index 246b3cb721..4b2e46950f 100644
--- a/web/client/components/widgets/widget/__tests__/LegendWidget-test.jsx
+++ b/web/client/components/widgets/widget/__tests__/LegendWidget-test.jsx
@@ -26,6 +26,14 @@ describe('LegendWidget component', () => {
const container = document.getElementById('container');
const el = container.querySelector('.mapstore-widget-card');
expect(el).toExist();
+ expect(container.querySelector('.glyphicon-pencil')).toExist();
+ expect(container.querySelector('.glyphicon-trash')).toExist();
+ });
+ it('view only mode', () => {
+ ReactDOM.render(, document.getElementById("container"));
+ const container = document.getElementById('container');
+ expect(container.querySelector('.glyphicon-pencil')).toNotExist();
+ expect(container.querySelector('.glyphicon-trash')).toNotExist();
});
it('Test LegendWidget onEdit callback', () => {
const actions = {
diff --git a/web/client/components/widgets/widget/__tests__/MapWidget-test.jsx b/web/client/components/widgets/widget/__tests__/MapWidget-test.jsx
index eb5c1d9fc6..12afc5a0ae 100644
--- a/web/client/components/widgets/widget/__tests__/MapWidget-test.jsx
+++ b/web/client/components/widgets/widget/__tests__/MapWidget-test.jsx
@@ -21,8 +21,15 @@ describe('MapWidget component', () => {
setTimeout(done);
});
it('MapWidget rendering with defaults', () => {
- ReactDOM.render( {}, getState: () => ({maptype: {mapType: 'openlayers'}})}} >, document.getElementById("container"));
- const el = document.querySelector('div');
- expect(el).toExist();
+ ReactDOM.render( {}, getState: () => ({maptype: {mapType: 'openlayers'}})}} >, document.getElementById("container"));
+ const container = document.getElementById('container');
+ expect(container.querySelector('.glyphicon-pencil')).toExist();
+ expect(container.querySelector('.glyphicon-trash')).toExist();
+ });
+ it('view only mode', () => {
+ ReactDOM.render( { }, getState: () => ({ maptype: { mapType: 'openlayers' } }) }} >, document.getElementById("container"));
+ const container = document.getElementById('container');
+ expect(container.querySelector('.glyphicon-pencil')).toNotExist();
+ expect(container.querySelector('.glyphicon-trash')).toNotExist();
});
});
diff --git a/web/client/components/widgets/widget/__tests__/TableWidget-test.jsx b/web/client/components/widgets/widget/__tests__/TableWidget-test.jsx
index 2117755cc6..415bf05062 100644
--- a/web/client/components/widgets/widget/__tests__/TableWidget-test.jsx
+++ b/web/client/components/widgets/widget/__tests__/TableWidget-test.jsx
@@ -36,6 +36,14 @@ describe('TableWidget component', () => {
const container = document.getElementById('container');
const el = container.querySelector('.mapstore-widget-card');
expect(el).toExist();
+ expect(container.querySelector('.glyphicon-pencil')).toExist();
+ expect(container.querySelector('.glyphicon-trash')).toExist();
+ });
+ it('view only mode', () => {
+ ReactDOM.render(, document.getElementById("container"));
+ const container = document.getElementById('container');
+ expect(container.querySelector('.glyphicon-pencil')).toNotExist();
+ expect(container.querySelector('.glyphicon-trash')).toNotExist();
});
it('Test TableWidget onEdit callback', () => {
const actions = {
diff --git a/web/client/components/widgets/widget/__tests__/TextWidget-test.jsx b/web/client/components/widgets/widget/__tests__/TextWidget-test.jsx
new file mode 100644
index 0000000000..40c20475c5
--- /dev/null
+++ b/web/client/components/widgets/widget/__tests__/TextWidget-test.jsx
@@ -0,0 +1,57 @@
+/*
+ * 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 React = require('react');
+const ReactDOM = require('react-dom');
+const ReactTestUtils = require('react-dom/test-utils');
+
+const expect = require('expect');
+const TextWidget = require('../TextWidget');
+
+describe('TextWidget component', () => {
+ beforeEach((done) => {
+ document.body.innerHTML = '';
+ setTimeout(done);
+ });
+ afterEach((done) => {
+ ReactDOM.unmountComponentAtNode(document.getElementById("container"));
+ document.body.innerHTML = '';
+ setTimeout(done);
+ });
+ it('rendering with defaults', () => {
+ ReactDOM.render(, document.getElementById("container"));
+ const container = document.getElementById('container');
+ const el = container.querySelector('.mapstore-widget-card');
+ expect(container.querySelector('.glyphicon-pencil')).toExist();
+ expect(container.querySelector('.glyphicon-trash')).toExist();
+ expect(el).toExist();
+ });
+ it('view only mode', () => {
+ ReactDOM.render(, document.getElementById("container"));
+ const container = document.getElementById('container');
+ expect(container.querySelector('.glyphicon-pencil')).toNotExist();
+ expect(container.querySelector('.glyphicon-trash')).toNotExist();
+ });
+ it('onEdit callback', () => {
+ const actions = {
+ onEdit: () => {}
+ };
+ const spyonEdit = expect.spyOn(actions, 'onEdit');
+ ReactDOM.render(, document.getElementById("container"));
+ const container = document.getElementById('container');
+ const el = container.querySelector('.glyphicon-pencil');
+ ReactTestUtils.Simulate.click(el); // <-- trigger event callback
+ expect(spyonEdit).toHaveBeenCalled();
+ });
+ it('rendering text', () => {
+ const TEST_TEXT = ' TEST
';
+ ReactDOM.render(, document.getElementById("container"));
+ const container = document.getElementById('container');
+ expect(container.querySelector('#TEST_TEXT')).toExist();
+ });
+});
diff --git a/web/client/epics/dashboard.js b/web/client/epics/dashboard.js
index d8da36ad1f..e49ad82372 100644
--- a/web/client/epics/dashboard.js
+++ b/web/client/epics/dashboard.js
@@ -37,7 +37,8 @@ const {
QUERY_FORM_SEARCH
} = require('../actions/queryform');
const {
- LOGIN_SUCCESS
+ LOGIN_SUCCESS,
+ LOGOUT
} = require('../actions/security');
const {
isDashboardEditing,
@@ -153,12 +154,7 @@ module.exports = {
return Rx.Observable.of(error({
title: "dashboard.errors.loading.title",
message: "dashboard.errors.loading.pleaseLogin"
- }))
- .merge(action$
- .ofType(LOGIN_SUCCESS)
- .switchMap( () => Rx.Observable.of(loadDashboard(id)).delay(1000))
- .filter(() => isDashboardAvailable(getState()))
- .takeUntil(action$.ofType(LOCATION_CHANGE)));
+ }));
} if (e.status === 404) {
return Rx.Observable.of(error({
title: "dashboard.errors.loading.title",
@@ -172,6 +168,13 @@ module.exports = {
}
))
),
+ reloadDashboardOnLoginLogout: (action$) =>
+ action$.ofType(LOAD_DASHBOARD).switchMap(
+ ({ id }) => action$
+ .ofType(LOGIN_SUCCESS, LOGOUT)
+ .switchMap(() => Rx.Observable.of(loadDashboard(id)).delay(1000))
+ .takeUntil(action$.ofType(LOCATION_CHANGE))
+ ),
// saving dashboard flow (both creation and update)
saveDashboard: action$ => action$
.ofType(SAVE_DASHBOARD)
diff --git a/web/client/plugins/Dashboard.jsx b/web/client/plugins/Dashboard.jsx
index 2ae0be8a7a..6949d8b7f4 100644
--- a/web/client/plugins/Dashboard.jsx
+++ b/web/client/plugins/Dashboard.jsx
@@ -7,21 +7,20 @@
*/
const React = require('react');
-const {get} = require('lodash');
-const {connect} = require('react-redux');
+const { get } = require('lodash');
+const { connect } = require('react-redux');
const { compose, withProps, withHandlers } = require('recompose');
-const {createSelector} = require('reselect');
-const {mapIdSelector} = require('../selectors/map');
-const { getDashboardWidgets, dependenciesSelector, getDashboardWidgetsLayout, isWidgetSelectionActive, getEditingWidget, getWidgetsDependenciesGroups} = require('../selectors/widgets');
-const { editWidget, updateWidgetProperty, deleteWidget, changeLayout, exportCSV, exportImage, selectWidget} = require('../actions/widgets');
-const {showConnectionsSelector} = require('../selectors/dashboard');
+const { createSelector } = require('reselect');
+const { getDashboardWidgets, dependenciesSelector, getDashboardWidgetsLayout, isWidgetSelectionActive, getEditingWidget, getWidgetsDependenciesGroups } = require('../selectors/widgets');
+const { editWidget, updateWidgetProperty, deleteWidget, changeLayout, exportCSV, exportImage, selectWidget } = require('../actions/widgets');
+const { showConnectionsSelector, dashboardResource, isDashboardLoading } = require('../selectors/dashboard');
const ContainerDimensions = require('react-container-dimensions').default;
const PropTypes = require('prop-types');
const WidgetsView = compose(
connect(
createSelector(
- mapIdSelector,
+ dashboardResource,
getDashboardWidgets,
getDashboardWidgetsLayout,
dependenciesSelector,
@@ -29,8 +28,11 @@ const WidgetsView = compose(
(state) => get(getEditingWidget(state), "id"),
getWidgetsDependenciesGroups,
showConnectionsSelector,
- (id, widgets, layouts, dependencies, selectionActive, editingWidgetId, groups, showGroupColor) => ({
- id,
+ isDashboardLoading,
+ (resource, widgets, layouts, dependencies, selectionActive, editingWidgetId, groups, showGroupColor, loading) => ({
+ resource,
+ loading,
+ canEdit: (resource ? !!resource.canEdit : true),
widgets,
layouts,
dependencies,
@@ -63,14 +65,14 @@ const WidgetsView = compose(
class Widgets extends React.Component {
- static propTypes = {
- enabled: PropTypes.bool
- };
- static defaultProps = {
- enabled: true
- };
+ static propTypes = {
+ enabled: PropTypes.bool
+ };
+ static defaultProps = {
+ enabled: true
+ };
render() {
- return this.props.enabled ? ({({width, height}) => } ) : null;
+ return this.props.enabled ? ({({ width, height }) => }) : null;
}
}
diff --git a/web/client/plugins/DashboardEditor.jsx b/web/client/plugins/DashboardEditor.jsx
index f2cdb5996f..04903396dc 100644
--- a/web/client/plugins/DashboardEditor.jsx
+++ b/web/client/plugins/DashboardEditor.jsx
@@ -7,26 +7,27 @@
*/
const React = require('react');
-const {withProps, compose} = require('recompose');
-const {createSelector} = require('reselect');
-const {connect} = require('react-redux');
+const { withProps, compose } = require('recompose');
+const { createSelector } = require('reselect');
+const { connect } = require('react-redux');
const PropTypes = require('prop-types');
-const { isDashboardEditing} = require('../selectors/dashboard');
+const { isDashboardEditing } = require('../selectors/dashboard');
const { isLoggedIn } = require('../selectors/security');
-const { dashboardHasWidgets } = require('../selectors/widgets');
-const { showConnectionsSelector, dashboardResource } = require('../selectors/dashboard');
-const {dashboardSelector} = require('./widgetbuilder/commons');
+const { dashboardHasWidgets, getWidgetsDependenciesGroups } = require('../selectors/widgets');
+const { showConnectionsSelector, dashboardResource, isDashboardLoading } = require('../selectors/dashboard');
+const { dashboardSelector } = require('./widgetbuilder/commons');
const { createWidget, toggleConnection } = require('../actions/widgets');
const { triggerShowConnections, triggerSave } = require('../actions/dashboard');
const withDashboardExitButton = require('./widgetbuilder/enhancers/withDashboardExitButton');
+const LoadingSpinner = require('../components/misc/LoadingSpinner');
const Builder =
compose(
- connect(dashboardSelector, { toggleConnection, triggerShowConnections}),
- withProps(({ availableDependencies = []}) => ({
+ connect(dashboardSelector, { toggleConnection, triggerShowConnections }),
+ withProps(({ availableDependencies = [] }) => ({
availableDependencies: availableDependencies.filter(d => d !== "map")
})),
withDashboardExitButton
@@ -39,11 +40,14 @@ const Toolbar = compose(
isLoggedIn,
dashboardResource,
dashboardHasWidgets,
- (showConnections, logged, resource, hasWidgets) => ({
+ getWidgetsDependenciesGroups,
+ (showConnections, logged, resource, hasWidgets, groups = []) => ({
showConnections,
+ hasConnections: groups.length > 0,
hasWidgets,
+ canEdit: (resource ? resource.canEdit : true),
canSave: logged && hasWidgets && (resource ? resource.canEdit : true)
- })
+ })
),
{
onShowConnections: triggerShowConnections,
@@ -52,69 +56,74 @@ const Toolbar = compose(
}
),
withProps(({
- onAddWidget = () => {},
- onToggleSave = () => {},
+ onAddWidget = () => { },
+ onToggleSave = () => { },
hasWidgets,
canSave,
- showConnections, onShowConnections = () => { }
- }) => ({
- buttons: [{
- glyph: 'plus',
- tooltipId: 'dashboard.editor.addACardToTheDashboard',
- bsStyle: 'primary',
- visible: true,
- onClick: () => onAddWidget()
- }, {
- glyph: 'floppy-disk',
- tooltipId: 'dashboard.editor.save',
- bsStyle: 'primary',
- tooltipPosition: 'right',
- visible: !!canSave,
- onClick: () => onToggleSave(true)
- }, {
- glyph: showConnections ? 'bulb-on' : 'bulb-off',
- tooltipId: showConnections ? 'dashboard.editor.hideConnections' : 'dashboard.editor.showConnections',
- bsStyle: showConnections ? 'success' : 'primary',
- visible: !!hasWidgets,
- onClick: () => onShowConnections(!showConnections)
- }]
- }))
+ canEdit,
+ hasConnections,
+ showConnections,
+ onShowConnections = () => { }
+ }) => ({
+ buttons: [{
+ glyph: 'plus',
+ tooltipId: 'dashboard.editor.addACardToTheDashboard',
+ bsStyle: 'primary',
+ visible: canEdit,
+ onClick: () => onAddWidget()
+ }, {
+ glyph: 'floppy-disk',
+ tooltipId: 'dashboard.editor.save',
+ bsStyle: 'primary',
+ tooltipPosition: 'right',
+ visible: !!canSave,
+ onClick: () => onToggleSave(true)
+ }, {
+ glyph: showConnections ? 'bulb-on' : 'bulb-off',
+ tooltipId: showConnections ? 'dashboard.editor.hideConnections' : 'dashboard.editor.showConnections',
+ bsStyle: showConnections ? 'success' : 'primary',
+ visible: !!hasWidgets && !!hasConnections,
+ onClick: () => onShowConnections(!showConnections)
+ }]
+ }))
)(require('../components/misc/toolbar/Toolbar'));
const SaveDialog = require('./dashboard/SaveDialog');
-const {setEditing, setEditorAvailable} = require('../actions/dashboard');
+const { setEditing, setEditorAvailable } = require('../actions/dashboard');
class DashboardEditorComponent extends React.Component {
- static propTypes = {
- id: PropTypes.string,
- editing: PropTypes.bool,
- limitDockHeight: PropTypes.bool,
- fluid: PropTypes.bool,
- zIndex: PropTypes.number,
- dockSize: PropTypes.number,
- position: PropTypes.string,
- onMount: PropTypes.func,
- onUnmount: PropTypes.func,
- setEditing: PropTypes.func,
- dimMode: PropTypes.string,
- src: PropTypes.string,
- style: PropTypes.object
- };
- static defaultProps = {
- id: "dashboard-editor",
- editing: false,
- dockSize: 500,
- limitDockHeight: true,
- zIndex: 10000,
- fluid: false,
- dimMode: "none",
- position: "left",
- onMount: () => {},
- onUnmount: () => {},
- setEditing: () => {}
- };
+ static propTypes = {
+ id: PropTypes.string,
+ editing: PropTypes.bool,
+ loading: PropTypes.bool,
+ limitDockHeight: PropTypes.bool,
+ fluid: PropTypes.bool,
+ zIndex: PropTypes.number,
+ dockSize: PropTypes.number,
+ position: PropTypes.string,
+ onMount: PropTypes.func,
+ onUnmount: PropTypes.func,
+ setEditing: PropTypes.func,
+ dimMode: PropTypes.string,
+ src: PropTypes.string,
+ style: PropTypes.object
+ };
+ static defaultProps = {
+ id: "dashboard-editor",
+ editing: false,
+ dockSize: 500,
+ loading: true,
+ limitDockHeight: true,
+ zIndex: 10000,
+ fluid: false,
+ dimMode: "none",
+ position: "left",
+ onMount: () => { },
+ onUnmount: () => { },
+ setEditing: () => { }
+ };
componentDidMount() {
this.props.onMount();
}
@@ -124,18 +133,20 @@ class DashboardEditorComponent extends React.Component {
}
render() {
return this.props.editing
- ? this.props.setEditing(false)} catalog={this.props.catalog}/>
- : (
-
-
-
);
+ ? this.props.setEditing(false)} catalog={this.props.catalog} />
+ : (
+
+
+ {this.props.loading ? : null}
+
);
}
}
const Plugin = connect(
createSelector(
isDashboardEditing,
- (editing) => ({ editing }),
+ isDashboardLoading,
+ (editing, loading) => ({ editing, loading }),
), {
setEditing,
onMount: () => setEditorAvailable(true),