Update fauxton/navigation to use redux (#1142)
* Use redux
* Update tests
diff --git a/app/addons/fauxton/appwrapper.js b/app/addons/fauxton/appwrapper.js
index fca8a81..ea81a67 100644
--- a/app/addons/fauxton/appwrapper.js
+++ b/app/addons/fauxton/appwrapper.js
@@ -11,16 +11,14 @@
// the License.
import React from 'react';
+import { connect } from 'react-redux';
import GlobalNotificationsContainer from './notifications/components/GlobalNotificationsContainer';
import NotificationPanelContainer from './notifications/components/NotificationPanelContainer';
import PermanentNotificationContainer from './notifications/components/PermanentNotificationContainer';
import NavBar from './navigation/container/NavBar';
-import NavbarActions from './navigation/actions';
-import Stores from './navigation/stores';
+import * as NavbarActions from './navigation/actions';
import classNames from 'classnames';
-const navBarStore = Stores.navBarStore;
-
class ContentWrapper extends React.Component {
constructor(props) {
super(props);
@@ -29,14 +27,14 @@
};
if (props.router.currentRouteOptions && props.router.currentRouteOptions.selectedHeader) {
- NavbarActions.setNavbarActiveLink(this.state.routerOptions.selectedHeader);
+ this.props.setNavbarActiveLink(this.state.routerOptions.selectedHeader);
}
}
componentDidMount () {
this.props.router.on('new-component', (routerOptions) => {
this.setState({routerOptions});
- NavbarActions.setNavbarActiveLink(this.state.routerOptions.selectedHeader);
+ this.props.setNavbarActiveLink(this.state.routerOptions.selectedHeader);
});
this.props.router.on('trigger-update', () => {
@@ -55,31 +53,15 @@
}
}
-export default class App extends React.Component {
+class App extends React.Component {
constructor (props) {
super(props);
- this.state = this.getStoreState();
- }
-
- getStoreState () {
- return {
- isPrimaryNavMinimized: navBarStore.isMinimized()
- };
- }
-
- componentDidMount () {
- navBarStore.on('change', this.onChange, this);
- }
-
- onChange () {
- this.setState(this.getStoreState());
}
render () {
const mainClass = classNames(
- {'closeMenu': this.state.isPrimaryNavMinimized}
+ {'closeMenu': this.props.isPrimaryNavMinimized}
);
-
return (
<div>
<PermanentNotificationContainer />
@@ -91,7 +73,7 @@
<div id="app-container">
<div className="wrapper">
<div className="pusher">
- <ContentWrapper router={this.props.router} />
+ <ContentWrapper router={this.props.router} setNavbarActiveLink={this.props.setNavbarActiveLink}/>
</div>
<div id="primary-navbar">
<NavBar/>
@@ -103,3 +85,15 @@
);
}
}
+
+export default connect(
+ ({ navigation }) => {
+ return {
+ isPrimaryNavMinimized: navigation.isMinimized};
+ },
+ (dispatch) => {
+ return {
+ setNavbarActiveLink: (link) => { dispatch(NavbarActions.setNavbarActiveLink(link)); }
+ };
+ }
+)(App);
diff --git a/app/addons/fauxton/base.js b/app/addons/fauxton/base.js
index 8a3f417..a1bb98e 100644
--- a/app/addons/fauxton/base.js
+++ b/app/addons/fauxton/base.js
@@ -12,7 +12,8 @@
import app from '../../app';
import FauxtonAPI from '../../core/api';
-import NavigationActions from './navigation/actions';
+import * as NavigationActions from './navigation/actions';
+import navigationReducers from './navigation/reducers';
import notificationsReducer from './notifications/reducers';
import './assets/less/fauxton.less';
@@ -32,6 +33,7 @@
});
FauxtonAPI.addReducers({
+ navigation: navigationReducers,
notifications: notificationsReducer
});
diff --git a/app/addons/fauxton/navigation/__tests__/login-logout-test.js b/app/addons/fauxton/navigation/__tests__/login-logout-test.js
index 097e495..e84c2af 100644
--- a/app/addons/fauxton/navigation/__tests__/login-logout-test.js
+++ b/app/addons/fauxton/navigation/__tests__/login-logout-test.js
@@ -13,25 +13,23 @@
import React from 'react';
import { mount } from 'enzyme';
-
import NavBar from '../components/NavBar';
describe('Navigation Bar', () => {
-
+ const defaultProps = {
+ activeLink: '',
+ isMinimized: false,
+ version: '42',
+ navLinks: [],
+ bottomNavLinks: [],
+ footerNavLinks: [],
+ isNavBarVisible: true,
+ isLoginSectionVisible: true,
+ isLoginVisibleInsteadOfLogout: true,
+ toggleNavbarMenu: () => {}
+ };
it('renders with login button when logged out', () => {
- const props = {
- activeLink: '',
- isMinimized: false,
- version: '42',
- navLinks: [],
- bottomNavLinks: [],
- footerNavLinks: [],
- isNavBarVisible: true,
- isLoginSectionVisible: true,
- isLoginVisibleInsteadOfLogout: true
- };
-
- const navBar = mount(<NavBar {...props} />);
+ const navBar = mount(<NavBar {...defaultProps} />);
const button = navBar.find('[href="#/login"]');
expect(button.text()).toContain('Login');
@@ -39,19 +37,11 @@
it('renders with logout button when logged in', () => {
const props = {
- activeLink: '',
- isMinimized: false,
- version: '42',
- navLinks: [],
- bottomNavLinks: [],
- footerNavLinks: [],
username: 'Rocko',
- isNavBarVisible: true,
- isLoginSectionVisible: true,
isLoginVisibleInsteadOfLogout: false
};
- const navBar = mount(<NavBar {...props} />);
+ const navBar = mount(<NavBar {...defaultProps} {...props} />);
const button = navBar.find('[href="#/logout"]');
expect(button.text()).toContain('Log Out');
@@ -59,19 +49,12 @@
it('Admin Party has no Logout button and no Login button', () => {
const props = {
- activeLink: '',
- isMinimized: false,
- version: '42',
- navLinks: [],
- bottomNavLinks: [],
- footerNavLinks: [],
username: 'Rocko',
- isNavBarVisible: true,
isLoginSectionVisible: false,
isLoginVisibleInsteadOfLogout: false
};
- const navBar = mount(<NavBar {...props} />);
+ const navBar = mount(<NavBar {...defaultProps} {...props} />);
expect(navBar.text()).not.toMatch(/Login/);
expect(navBar.text()).not.toMatch(/Log Out/);
diff --git a/app/addons/fauxton/navigation/__tests__/navbar-test.js b/app/addons/fauxton/navigation/__tests__/navbar-test.js
index 769b945..ba52483 100644
--- a/app/addons/fauxton/navigation/__tests__/navbar-test.js
+++ b/app/addons/fauxton/navigation/__tests__/navbar-test.js
@@ -9,58 +9,60 @@
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
-import NavBarContainer from "../container/NavBar";
-import FauxtonAPI from "../../../../core/api";
-import ActionTypes from "../actiontypes";
-import React from "react";
-import ReactDOM from "react-dom";
+
import {mount} from 'enzyme';
+import React from 'react';
+import NavBar from '../components/NavBar';
describe('Navigation Bar', () => {
- FauxtonAPI.session = {
- user: () => {}
+ const defaultProps = {
+ isMinimized: true,
+ version: '',
+ username: '',
+ navLinks: [],
+ bottomNavLinks: [],
+ footerNavLinks: [],
+ isNavBarVisible: true,
+ isLoginSectionVisible: false,
+ isLoginVisibleInsteadOfLogout: true,
+ toggleNavbarMenu: () => {}
};
it('is displayed by default', () => {
- const navbar = mount(<NavBarContainer />);
+ const navbar = mount(<NavBar {...defaultProps}/>);
expect(navbar.find('.faux-navbar').length).toBe(1);
});
it('is dynamically displayed by isNavBarVisible', () => {
- const navbar = mount(<NavBarContainer />);
+ const navbar = mount(<NavBar
+ {...defaultProps}
+ isNavBarVisible={false}/>);
- FauxtonAPI.dispatch({
- type: ActionTypes.NAVBAR_HIDE
- });
- navbar.update();
expect(navbar.find('.faux-navbar').length).toBe(0);
- FauxtonAPI.dispatch({
- type: ActionTypes.NAVBAR_SHOW
- });
+ navbar.setProps({ isNavBarVisible: true });
navbar.update();
expect(navbar.find('.faux-navbar').length).toBe(1);
});
it('can display items with icon badge', () => {
- FauxtonAPI.dispatch({
- type: ActionTypes.ADD_NAVBAR_LINK,
- link: {
+ const navLinks = [
+ {
href: "#/_with_badge",
title: "WithBadge",
icon: "fonticon-database",
badge: true
- }
- });
- FauxtonAPI.dispatch({
- type: ActionTypes.ADD_NAVBAR_LINK,
- link: {
+ },
+ {
href: "#/_without_badge",
title: "WithoutBadge",
icon: "fonticon-database"
}
- });
- const navbar = mount(<NavBarContainer />);
+ ];
+
+ const navbar = mount(<NavBar
+ {...defaultProps}
+ navLinks={navLinks}/>);
expect(navbar.find('div[data-nav-name="WithoutBadge"] i.faux-navbar__icon-badge').length, 0);
expect(navbar.find('div[data-nav-name="WithBadge"] i.faux-navbar__icon-badge').length, 1);
});
diff --git a/app/addons/fauxton/navigation/__tests__/navigation-reducers.js b/app/addons/fauxton/navigation/__tests__/navigation-reducers.js
new file mode 100644
index 0000000..f2f9f12
--- /dev/null
+++ b/app/addons/fauxton/navigation/__tests__/navigation-reducers.js
@@ -0,0 +1,185 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+import ActionTypes from '../actiontypes';
+import reducer from '../reducers';
+
+describe('NavBar Reducer', () => {
+
+ describe('add links', () => {
+
+ it('to nav links', () => {
+ const action = {
+ type: ActionTypes.ADD_NAVBAR_LINK,
+ link: {
+ id: 'mylink'
+ }
+ };
+ const newState = reducer(undefined, action);
+ expect(newState.navLinks[0].id).toMatch(action.link.id);
+ });
+
+ it('to top nav links', () => {
+ const action1 = {
+ type: ActionTypes.ADD_NAVBAR_LINK,
+ link: { id: 'mylink1' }
+ };
+ const action2 = {
+ type: ActionTypes.ADD_NAVBAR_LINK,
+ link: { id: 'mylink2', top: true }
+ };
+ let newState = reducer(undefined, action1);
+ newState = reducer(newState, action2);
+
+ expect(newState.navLinks[0].id).toMatch(action2.link.id);
+ });
+
+ it('to bottom nav', () => {
+ const action = {
+ type: ActionTypes.ADD_NAVBAR_LINK,
+ link: {
+ id: 'bottomNav',
+ bottomNav: true
+ }
+ };
+ const newState = reducer(undefined, action);
+ expect(newState.bottomNavLinks[0].id).toMatch(action.link.id);
+ });
+
+ it('to top of bottom nav', () => {
+ const action = {
+ type: ActionTypes.ADD_NAVBAR_LINK,
+ link: {
+ id: 'bottomNav',
+ bottomNav: true,
+ top: true
+ }
+ };
+ const newState = reducer(undefined, action);
+ expect(newState.bottomNavLinks[0].id).toMatch(action.link.id);
+ });
+
+ it('to footer nav', () => {
+ const action = {
+ type: ActionTypes.ADD_NAVBAR_LINK,
+ link: {
+ id: 'footerNav',
+ footerNav: true
+ }
+ };
+ const newState = reducer(undefined, action);
+ expect(newState.footerNavLinks[0].id).toMatch(action.link.id);
+ });
+ });
+
+ describe('remove link', () => {
+ it('from nav links', () => {
+ const action = {
+ type: ActionTypes.ADD_NAVBAR_LINK,
+ link: {
+ id: 'remove_link'
+ }
+ };
+ let newState = reducer(undefined, action);
+ action.type = ActionTypes.REMOVE_NAVBAR_LINK;
+ newState = reducer(newState, action);
+
+ expect(newState.navLinks.length).toBe(0);
+ });
+
+ it('remove link from list only when it exists', () => {
+ const addAction = {
+ type: ActionTypes.ADD_NAVBAR_LINK,
+ link: {
+ id: 'remove_link1',
+ footerNav: true
+ }
+ };
+ let newState = reducer(undefined, addAction);
+ addAction.link = { id: 'remove_link2', footerNav: true };
+ newState = reducer(newState, addAction);
+ addAction.link = { id: 'remove_link3', footerNav: true };
+ newState = reducer(newState, addAction);
+ expect(newState.footerNavLinks.length).toBe(3);
+
+ const removeAction = {
+ type: ActionTypes.REMOVE_NAVBAR_LINK,
+ link: addAction.link
+ };
+ newState = reducer(newState, removeAction);
+ newState = reducer(newState, removeAction);
+ newState = reducer(newState, removeAction);
+ expect(newState.footerNavLinks.length).toBe(2);
+ });
+
+ it('from bottom nav links', () => {
+ const action = {
+ type: ActionTypes.ADD_NAVBAR_LINK,
+ link: {
+ id: 'remove_link',
+ bottomNav: true
+ }
+ };
+ let newState = reducer(undefined, action);
+ action.type = ActionTypes.REMOVE_NAVBAR_LINK;
+ newState = reducer(newState, action);
+
+ expect(newState.bottomNavLinks.length).toBe(0);
+ });
+
+ it('from footer nav links', () => {
+ const action = {
+ type: ActionTypes.ADD_NAVBAR_LINK,
+ link: {
+ id: 'remove_link',
+ footerNav: true
+ }
+ };
+ let newState = reducer(undefined, action);
+ action.type = ActionTypes.REMOVE_NAVBAR_LINK;
+ newState = reducer(newState, action);
+
+ expect(newState.footerNavLinks.length).toBe(0);
+ });
+ });
+
+ describe('update link', () => {
+ it('for nav links', () => {
+ const action = {
+ type: ActionTypes.ADD_NAVBAR_LINK,
+ link: {
+ id: 'update-link',
+ title: 'first'
+ }
+ };
+ let newState = reducer(undefined, action);
+ action.type = ActionTypes.UPDATE_NAVBAR_LINK;
+ action.link.title = 'second';
+ newState = reducer(newState, action);
+
+ expect(newState.navLinks[0].title).toMatch('second');
+ });
+
+ });
+
+ describe('set version', () => {
+ it('stores version number', () => {
+ const action = {
+ type: ActionTypes.NAVBAR_SET_VERSION_INFO,
+ version: 1234
+ };
+ const newState = reducer(undefined, action);
+ expect(newState.version).toBe(1234);
+ });
+
+ });
+});
diff --git a/app/addons/fauxton/navigation/__tests__/navigation-store-test.js b/app/addons/fauxton/navigation/__tests__/navigation-store-test.js
deleted file mode 100644
index c664dce..0000000
--- a/app/addons/fauxton/navigation/__tests__/navigation-store-test.js
+++ /dev/null
@@ -1,221 +0,0 @@
-// Licensed under the Apache License, Version 2.0 (the "License"); you may not
-// use this file except in compliance with the License. You may obtain a copy of
-// the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-// License for the specific language governing permissions and limitations under
-// the License.
-import FauxtonAPI from "../../../../core/api";
-import Stores from "../stores";
-const navBarStore = Stores.navBarStore;
-
-describe('NavBarStore', () => {
- beforeEach(() => {
- FauxtonAPI.dispatch({
- type: 'CLEAR_NAVBAR_LINK',
- });
-
- });
-
- describe('add links', () => {
-
- it('to nav links', () => {
- var link = {
- id: 'mylink'
- };
- FauxtonAPI.dispatch({
- type: 'ADD_NAVBAR_LINK',
- link: link
- });
-
- expect(navBarStore.getNavLinks()[0].id).toMatch(link.id);
- });
-
- it('to top nav links', () => {
- var link1 = {
- id: 'mylink1'
- };
-
- var link2 = {
- id: 'mylink2',
- top: true
- };
-
- FauxtonAPI.dispatch({
- type: 'ADD_NAVBAR_LINK',
- link: link1
- });
-
- FauxtonAPI.dispatch({
- type: 'ADD_NAVBAR_LINK',
- link: link2
- });
-
- expect(navBarStore.getNavLinks()[0].id).toMatch(link2.id);
- });
-
- it('to bottom nav', () => {
- var link = {
- id: 'bottomNav',
- bottomNav: true
- };
- FauxtonAPI.dispatch({
- type: 'ADD_NAVBAR_LINK',
- link: link
- });
-
- expect(navBarStore.getBottomNavLinks()[0].id).toMatch(link.id);
- });
-
- it('to top of bottom nav', () => {
- var link = {
- id: 'bottomNav',
- bottomNav: true,
- top: true
- };
- FauxtonAPI.dispatch({
- type: 'ADD_NAVBAR_LINK',
- link: link
- });
-
- expect(navBarStore.getBottomNavLinks()[0].id).toMatch(link.id);
- });
-
- it('to footer nav', () => {
- var link = {
- id: 'footerNav',
- footerNav: true
- };
- FauxtonAPI.dispatch({
- type: 'ADD_NAVBAR_LINK',
- link: link
- });
-
- expect(navBarStore.getFooterNavLinks()[0].id).toMatch(link.id);
- });
- });
-
- describe('remove link', () => {
- it('from nav links', () => {
- var link = {
- id: 'remove_link',
- };
- FauxtonAPI.dispatch({
- type: 'ADD_NAVBAR_LINK',
- link: link
- });
-
- FauxtonAPI.dispatch({
- type: 'REMOVE_NAVBAR_LINK',
- link: link
- });
-
- expect(navBarStore.getNavLinks().length).toBe(0);
- });
-
- it('remove link from list', () => {
- const addLink = (id) => {
- FauxtonAPI.dispatch({
- type: 'ADD_NAVBAR_LINK',
- link: {
- id: id,
- footerNav: true
- }
- });
- };
- const removeLink = () => {
- FauxtonAPI.dispatch({
- type: 'REMOVE_NAVBAR_LINK',
- link: {
- id: 'remove_link3',
- footerNav: true
- }
- });
- };
- addLink('remove_link1');
- addLink('remove_link2');
- addLink('remove_link3');
-
- removeLink();
- removeLink();
- removeLink();
-
- expect(navBarStore.getFooterNavLinks().length).toBe(2);
- });
-
- it('from bottom nav links', () => {
- var link = {
- id: 'remove_link',
- bottomNav: true
- };
- FauxtonAPI.dispatch({
- type: 'ADD_NAVBAR_LINK',
- link: link
- });
-
- FauxtonAPI.dispatch({
- type: 'REMOVE_NAVBAR_LINK',
- link: link
- });
-
- expect(navBarStore.getBottomNavLinks().length).toBe(0);
- });
-
- it('from footer nav links', () => {
- var link = {
- id: 'remove_link',
- footerNav: true
- };
- FauxtonAPI.dispatch({
- type: 'ADD_NAVBAR_LINK',
- link: link
- });
-
- FauxtonAPI.dispatch({
- type: 'REMOVE_NAVBAR_LINK',
- link: link
- });
-
- expect(navBarStore.getFooterNavLinks().length).toBe(0);
- });
- });
-
- describe('update link', () => {
- it('for nav links', () => {
- var link = {
- id: 'update-link',
- title: 'first'
- };
- FauxtonAPI.dispatch({
- type: 'ADD_NAVBAR_LINK',
- link: link
- });
-
- link.title = 'second';
-
- FauxtonAPI.dispatch({
- type: 'UPDATE_NAVBAR_LINK',
- link: link
- });
-
- expect(navBarStore.getNavLinks()[0].title).toMatch('second');
- });
-
- });
-
- describe('set version', () => {
- it('stores version number', () => {
- FauxtonAPI.dispatch({
- type: 'NAVBAR_SET_VERSION_INFO',
- version: 1234
- });
-
- expect(navBarStore.getVersion()).toBe(1234);
- });
-
- });
-});
diff --git a/app/addons/fauxton/navigation/actions.js b/app/addons/fauxton/navigation/actions.js
index 3548157..ac26091 100644
--- a/app/addons/fauxton/navigation/actions.js
+++ b/app/addons/fauxton/navigation/actions.js
@@ -10,53 +10,51 @@
// License for the specific language governing permissions and limitations under
// the License.
-import FauxtonAPI from "../../../core/api";
-import ActionTypes from "./actiontypes";
+import FauxtonAPI from '../../../core/api';
+import ActionTypes from './actiontypes';
-export default {
- toggleNavbarMenu () {
- FauxtonAPI.dispatch({
- type: ActionTypes.TOGGLE_NAVBAR_MENU
- });
- },
+export const toggleNavbarMenu = () => (dispatch) => {
+ dispatch({
+ type: ActionTypes.TOGGLE_NAVBAR_MENU
+ });
+};
- addHeaderLink (link) {
- FauxtonAPI.dispatch({
- type: ActionTypes.ADD_NAVBAR_LINK,
- link: link
- });
- },
+export const addHeaderLink = (link) => (dispatch) => {
+ dispatch({
+ type: ActionTypes.ADD_NAVBAR_LINK,
+ link: link
+ });
+};
- removeHeaderLink (link) {
- FauxtonAPI.dispatch({
- type: ActionTypes.REMOVE_NAVBAR_LINK,
- link: link
- });
- },
+export const removeHeaderLink = (link) => (dispatch) => {
+ dispatch({
+ type: ActionTypes.REMOVE_NAVBAR_LINK,
+ link: link
+ });
+};
- setNavbarVersionInfo (versionInfo) {
- FauxtonAPI.dispatch({
- type: ActionTypes.NAVBAR_SET_VERSION_INFO,
- version: versionInfo
- });
- },
+export const setNavbarVersionInfo = (versionInfo) => {
+ FauxtonAPI.reduxDispatch({
+ type: ActionTypes.NAVBAR_SET_VERSION_INFO,
+ version: versionInfo
+ });
+};
- setNavbarActiveLink (header) {
- FauxtonAPI.dispatch({
- type: ActionTypes.NAVBAR_ACTIVE_LINK,
- name: header
- });
- },
+export const setNavbarActiveLink = (header) => (dispatch) => {
+ dispatch({
+ type: ActionTypes.NAVBAR_ACTIVE_LINK,
+ name: header
+ });
+};
- showNavBar () {
- FauxtonAPI.dispatch({
- type: ActionTypes.NAVBAR_SHOW
- });
- },
+export const showNavBar = () => (dispatch) => {
+ dispatch({
+ type: ActionTypes.NAVBAR_SHOW
+ });
+};
- hideNavBar () {
- FauxtonAPI.dispatch({
- type: ActionTypes.NAVBAR_HIDE
- });
- }
+export const hideNavBar = () => (dispatch) => {
+ dispatch({
+ type: ActionTypes.NAVBAR_HIDE
+ });
};
diff --git a/app/addons/fauxton/navigation/actiontypes.js b/app/addons/fauxton/navigation/actiontypes.js
index 2a5d72f..e007062 100644
--- a/app/addons/fauxton/navigation/actiontypes.js
+++ b/app/addons/fauxton/navigation/actiontypes.js
@@ -14,7 +14,6 @@
ADD_NAVBAR_LINK: 'ADD_NAVBAR_LINK',
TOGGLE_NAVBAR_MENU: 'TOGGLE_NAVBAR_MENU',
UPDATE_NAVBAR_LINK: 'UPDATE_NAVBAR_LINK',
- CLEAR_NAVBAR_LINK: 'CLEAR_NAVBAR_LINK',
REMOVE_NAVBAR_LINK: 'REMOVE_NAVBAR_LINK',
HIDE_NAVBAR_LINK_BADGE: 'HIDE_NAVBAR_LINK_BADGE',
SHOW_NAVBAR_LINK_BADGE: 'SHOW_NAVBAR_LINK_BADGE',
diff --git a/app/addons/fauxton/navigation/components/NavBar.js b/app/addons/fauxton/navigation/components/NavBar.js
index 77cb3ab..89d0c48 100644
--- a/app/addons/fauxton/navigation/components/NavBar.js
+++ b/app/addons/fauxton/navigation/components/NavBar.js
@@ -10,10 +10,9 @@
// License for the specific language governing permissions and limitations under
// the License.
+import classNames from 'classnames';
import PropTypes from 'prop-types';
-
import React, { Component } from 'react';
-
import Footer from './Footer';
import Burger from './Burger';
import NavLink from './NavLink';
@@ -21,12 +20,12 @@
import LogoutButton from './LogoutButton';
import LoginButton from './LoginButton';
-import Actions from "../actions";
-
-import classNames from 'classnames';
-
class NavBar extends Component {
+ constructor(props) {
+ super(props);
+ }
+
createLinks (links) {
const { activeLink, isMinimized } = this.props;
@@ -39,10 +38,6 @@
});
}
- toggleMenu () {
- Actions.toggleNavbarMenu();
- }
-
render () {
const {
isMinimized,
@@ -51,7 +46,8 @@
isLoginVisibleInsteadOfLogout,
activeLink,
username,
- isNavBarVisible
+ isNavBarVisible,
+ toggleNavbarMenu
} = this.props;
if (!isNavBarVisible) {
@@ -76,7 +72,7 @@
<div className={navClasses}>
<nav>
<div className="faux-navbar__linkcontainer">
- <Burger isMinimized={isMinimized} toggleMenu={this.toggleMenu}/>
+ <Burger isMinimized={isMinimized} toggleMenu={toggleNavbarMenu}/>
<div className="faux-navbar__links">
{navLinks}
{bottomNavLinks}
@@ -110,7 +106,8 @@
footerNavLinks: PropTypes.array,
isNavBarVisible: PropTypes.bool,
isLoginSectionVisible: PropTypes.bool.isRequired,
- isLoginVisibleInsteadOfLogout: PropTypes.bool.isRequired
+ isLoginVisibleInsteadOfLogout: PropTypes.bool.isRequired,
+ toggleNavbarMenu: PropTypes.func.isRequired
};
export default NavBar;
diff --git a/app/addons/fauxton/navigation/container/NavBar.js b/app/addons/fauxton/navigation/container/NavBar.js
index d5efb13..2703b79 100644
--- a/app/addons/fauxton/navigation/container/NavBar.js
+++ b/app/addons/fauxton/navigation/container/NavBar.js
@@ -10,55 +10,38 @@
// License for the specific language governing permissions and limitations under
// the License.
-import FauxtonAPI from "../../../../core/api";
-import React from "react";
-
-import ReactDOM from "react-dom";
-import Stores from "../stores";
-
+import { connect } from 'react-redux';
+import FauxtonAPI from '../../../../core/api';
import NavBar from '../components/NavBar';
+import * as Actions from '../actions';
-const navBarStore = Stores.navBarStore;
-
-class NavBarContainer extends React.Component {
- getStoreState = () => {
- return {
- navLinks: navBarStore.getNavLinks(),
- bottomNavLinks: navBarStore.getBottomNavLinks(),
- footerNavLinks: navBarStore.getFooterNavLinks(),
- activeLink: navBarStore.getActiveLink(),
- version: navBarStore.getVersion(),
- isMinimized: navBarStore.isMinimized(),
- isNavBarVisible: navBarStore.isNavBarVisible(),
-
- isLoginSectionVisible: navBarStore.getIsLoginSectionVisible(),
- isLoginVisibleInsteadOfLogout: navBarStore.getIsLoginVisibleInsteadOfLogout()
- };
+const mapStateToProps = ({ navigation }) => {
+ const user = FauxtonAPI.session.user();
+ return {
+ navLinks: navigation.navLinks,
+ bottomNavLinks: navigation.bottomNavLinks,
+ footerNavLinks: navigation.footerNavLinks,
+ activeLink: navigation.activeLink,
+ version: navigation.version,
+ isMinimized: navigation.isMinimized,
+ isNavBarVisible: navigation.navBarVisible,
+ isLoginSectionVisible: navigation.loginSectionVisible,
+ isLoginVisibleInsteadOfLogout: navigation.loginVisibleInsteadOfLogout,
+ username: (user && user.name) ? user.name : ''
};
+};
- onChange = () => {
- this.setState(this.getStoreState());
+const mapDispatchToProps = (dispatch) => {
+ return {
+ toggleNavbarMenu: () => {
+ dispatch(Actions.toggleNavbarMenu());
+ }
};
+};
- state = this.getStoreState();
-
- componentDidMount() {
- navBarStore.on('change', this.onChange, this);
- }
-
- componentWillUnmount() {
- navBarStore.off('change', this.onChange);
- }
-
- render() {
- const user = FauxtonAPI.session.user();
-
- const username = (user && user.name) ? user.name : '';
- return (
- <NavBar {...this.state} username={username} />
- );
- }
-}
-
+const NavBarContainer = connect(
+ mapStateToProps,
+ mapDispatchToProps
+)(NavBar);
export default NavBarContainer;
diff --git a/app/addons/fauxton/navigation/reducers.js b/app/addons/fauxton/navigation/reducers.js
new file mode 100644
index 0000000..6af5d2c
--- /dev/null
+++ b/app/addons/fauxton/navigation/reducers.js
@@ -0,0 +1,209 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+import ActionTypes from './actiontypes';
+
+const initialState = {
+ isMinimized: true,
+ activeLink: null,
+ version: null,
+ navLinks: [],
+ footerNavLinks: [],
+ bottomNavLinks: [],
+ navBarVisible: true,
+ loginSectionVisible: false,
+ loginVisibleInsteadOfLogout: true
+};
+
+function addLink(state, link) {
+ const newState = { ...state };
+
+ if (link.top && !link.bottomNav) {
+ newState.navLinks = [].concat(newState.navLinks);
+ newState.navLinks.unshift(link);
+ return newState;
+ }
+ if (link.top && link.bottomNav) {
+ newState.bottomNavLinks = [].concat(newState.bottomNavLinks);
+ newState.bottomNavLinks.unshift(link);
+ return newState;
+ }
+ if (link.bottomNav) {
+ newState.bottomNavLinks = [].concat(newState.bottomNavLinks);
+ newState.bottomNavLinks.push(link);
+ return newState;
+ }
+ if (link.footerNav) {
+ newState.footerNavLinks = [].concat(newState.footerNavLinks);
+ newState.footerNavLinks.push(link);
+ return newState;
+ }
+
+ newState.navLinks = [].concat(newState.navLinks);
+ newState.navLinks.push(link);
+ return newState;
+}
+
+function removeLink (state, removeLink) {
+ const {links, sectionName} = getLinkSection(state, removeLink);
+
+ // create new array without the link to remove
+ const newLinks = links.filter(link => link.id !== removeLink.id);
+
+ if (newLinks.length === links.length) {
+ return state;
+ }
+
+ const newState = { ...state };
+ newState[sectionName] = newLinks;
+ return newState;
+}
+
+function updateLink (state, link) {
+ const {links, sectionName} = getLinkSection(state, link);
+
+ // create new array and updates the link when found
+ let found = false;
+ const newLinks = links.map(el => {
+ if (el.id === link.id) {
+ found = true;
+ return {
+ ...el,
+ title: link.title,
+ href: link.href
+ };
+ }
+ return el;
+ });
+
+ if (!found) {
+ return state;
+ }
+
+ const newState = { ...state };
+ newState[sectionName] = newLinks;
+ return newState;
+}
+
+function setLinkBadgeVisible (state, link, visible) {
+ const {links, sectionName} = getLinkSection(state, link);
+
+ let found = false;
+ const newLinks = links.map(el => {
+ if (el.title === link.title) {
+ found = true;
+ return {
+ ...el,
+ badge: visible
+ };
+ }
+ return el;
+ });
+
+ if (!found) {
+ return state;
+ }
+
+ const newState = { ...state };
+ newState[sectionName] = newLinks;
+ return newState;
+}
+
+function getLinkSection (state, link) {
+ let links = state.navLinks;
+ let sectionName = 'navLinks';
+
+ if (link.bottomNav) {
+ links = state.bottomNavLinks;
+ sectionName = 'bottomNavLinks';
+ }
+
+ if (link.footerNav) {
+ links = state.footerNavLinks;
+ sectionName = 'footerNavLinks';
+ }
+
+ return { links, sectionName };
+}
+
+export default function navigation(state = initialState, action) {
+ switch (action.type) {
+
+ case ActionTypes.ADD_NAVBAR_LINK:
+ return addLink(state, action.link);
+
+ case ActionTypes.TOGGLE_NAVBAR_MENU:
+ return {
+ ...state,
+ isMinimized: !state.isMinimized
+ };
+
+ case ActionTypes.UPDATE_NAVBAR_LINK:
+ return updateLink(state, action.link);
+
+ case ActionTypes.REMOVE_NAVBAR_LINK:
+ return removeLink(state, action.link);
+
+ case ActionTypes.SHOW_NAVBAR_LINK_BADGE:
+ return setLinkBadgeVisible(state, action.link, true);
+
+ case ActionTypes.HIDE_NAVBAR_LINK_BADGE:
+ return setLinkBadgeVisible(state, action.link, false);
+
+ case ActionTypes.NAVBAR_SET_VERSION_INFO:
+ return {
+ ...state,
+ version: action.version
+ };
+
+ case ActionTypes.NAVBAR_ACTIVE_LINK:
+ return {
+ ...state,
+ activeLink: action.name
+ };
+
+ case ActionTypes.NAVBAR_HIDE:
+ return {
+ ...state,
+ navBarVisible: false
+ };
+
+ case ActionTypes.NAVBAR_SHOW:
+ return {
+ ...state,
+ navBarVisible: true
+ };
+
+ case ActionTypes.NAVBAR_SHOW_HIDE_LOGIN_LOGOUT_SECTION:
+ return {
+ ...state,
+ loginSectionVisible: action.visible
+ };
+
+ case ActionTypes.NAVBAR_SHOW_LOGIN_BUTTON:
+ return {
+ ...state,
+ loginSectionVisible: true,
+ loginVisibleInsteadOfLogout: true
+ };
+
+ case ActionTypes.NAVBAR_SHOW_LOGOUT_BUTTON:
+ return {
+ ...state,
+ loginSectionVisible: true,
+ loginVisibleInsteadOfLogout: false
+ };
+
+ default:
+ return state;
+ }
+}
diff --git a/app/addons/fauxton/navigation/stores.js b/app/addons/fauxton/navigation/stores.js
deleted file mode 100644
index d08f243..0000000
--- a/app/addons/fauxton/navigation/stores.js
+++ /dev/null
@@ -1,251 +0,0 @@
-// Licensed under the Apache License, Version 2.0 (the "License"); you may not
-// use this file except in compliance with the License. You may obtain a copy of
-// the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-// License for the specific language governing permissions and limitations under
-// the License.
-
-import FauxtonAPI from "../../../core/api";
-import ActionTypes from "./actiontypes";
-import {findIndex} from 'lodash';
-
-const Stores = {};
-
-Stores.NavBarStore = FauxtonAPI.Store.extend({
- initialize () {
- this.reset();
- },
-
- reset () {
- this._isMinimized = true;
- this._activeLink = null;
- this._version = null;
- this._navLinks = [];
- this._footerNavLinks = [];
- this._bottomNavLinks = [];
- this._navBarVisible = true;
-
- this._loginSectionVisible = false;
- this._loginVisibleInsteadOfLogout = true;
- },
-
- getIsLoginSectionVisible () {
- return this._loginSectionVisible;
- },
-
- getIsLoginVisibleInsteadOfLogout () {
- return this._loginVisibleInsteadOfLogout;
- },
-
- isNavBarVisible () {
- return this._navBarVisible;
- },
-
- showNavBar () {
- this._navBarVisible = true;
- },
-
- hideNavBar () {
- this._navBarVisible = false;
- },
-
- addLink (link) {
- if (link.top && !link.bottomNav) {
- this._navLinks.unshift(link);
- return;
- }
- if (link.top && link.bottomNav) {
- this._bottomNavLinks.unshift(link);
- return;
- }
- if (link.bottomNav) {
- this._bottomNavLinks.push(link);
- return;
- }
- if (link.footerNav) {
- this._footerNavLinks.push(link);
- return;
- }
-
- this._navLinks.push(link);
- },
-
- removeLink (removeLink) {
- const links = this.getLinkSection(removeLink);
-
- const indexOf = findIndex(links, link => {
- if (link.id === removeLink.id) {
- return true;
- }
-
- return false;
- });
-
- if (indexOf === -1) { return; }
-
- links.splice(indexOf, 1);
- },
-
- getNavLinks () {
- return this._navLinks;
- },
-
- getBottomNavLinks () {
- return this._bottomNavLinks;
- },
-
- getFooterNavLinks () {
- return this._footerNavLinks;
- },
-
- toggleMenu () {
- this._isMinimized = !this._isMinimized;
- },
-
- getLinkSection (link) {
- let links = this._navLinks;
-
- if (link.bottomNav) {
- links = this._bottomNavLinks;
- }
-
- if (link.footerNav) {
- links = this._footerNavLinks;
- }
-
- return links;
- },
-
- updateLink (link) {
- let oldLink;
- const links = this.getLinkSection(link);
-
- oldLink = _.find(links, function (oldLink) {
- return oldLink.id === link.id;
- });
-
- if (!oldLink) { return; }
-
- oldLink.title = link.title;
- oldLink.href = link.href;
- },
-
- showLinkBadge (link) {
- const links = this.getLinkSection(link);
- const selectedLink = links.find(function (oldLink) {
- return oldLink.title === link.title;
- });
- if (selectedLink) {
- selectedLink.badge = true;
- }
- },
-
- hideLinkBadge (link) {
- const links = this.getLinkSection(link);
- const selectedLink = links.find(function (oldLink) {
- return oldLink.title === link.title;
- });
-
- if (selectedLink) {
- selectedLink.badge = false;
- }
- },
-
- getVersion () {
- return this._version;
- },
-
- setVersion (version) {
- this._version = version;
- },
-
- getActiveLink () {
- return this._activeLink;
- },
-
- setActiveLink (activeLink) {
- this._activeLink = activeLink;
- },
-
- isMinimized () {
- return this._isMinimized;
- },
-
- dispatch (action) {
- switch (action.type) {
- case ActionTypes.ADD_NAVBAR_LINK:
- this.addLink(action.link);
- break;
-
- case ActionTypes.TOGGLE_NAVBAR_MENU:
- this.toggleMenu();
- break;
-
- case ActionTypes.UPDATE_NAVBAR_LINK:
- this.updateLink(action.link);
- break;
-
- case ActionTypes.CLEAR_NAVBAR_LINK:
- this.reset();
- break;
-
- case ActionTypes.REMOVE_NAVBAR_LINK:
- this.removeLink(action.link);
- break;
-
- case ActionTypes.SHOW_NAVBAR_LINK_BADGE:
- this.showLinkBadge(action.link);
- break;
-
- case ActionTypes.HIDE_NAVBAR_LINK_BADGE:
- this.hideLinkBadge(action.link);
- break;
-
- case ActionTypes.NAVBAR_SET_VERSION_INFO:
- this.setVersion(action.version);
- break;
-
- case ActionTypes.NAVBAR_ACTIVE_LINK:
- this.setActiveLink(action.name);
- break;
-
- case ActionTypes.NAVBAR_HIDE:
- this.hideNavBar();
- break;
-
- case ActionTypes.NAVBAR_SHOW:
- this.showNavBar();
- break;
-
- case ActionTypes.NAVBAR_SHOW_HIDE_LOGIN_LOGOUT_SECTION:
- this._loginSectionVisible = action.visible;
- break;
-
- case ActionTypes.NAVBAR_SHOW_LOGIN_BUTTON:
- this._loginSectionVisible = true;
- this._loginVisibleInsteadOfLogout = true;
- break;
-
- case ActionTypes.NAVBAR_SHOW_LOGOUT_BUTTON:
- this._loginSectionVisible = true;
- this._loginVisibleInsteadOfLogout = false;
- break;
-
- default:
- return;
- // do nothing
- }
-
- this.triggerChange();
- }
-});
-
-Stores.navBarStore = new Stores.NavBarStore();
-Stores.navBarStore.dispatchToken = FauxtonAPI.dispatcher.register(Stores.navBarStore.dispatch.bind(Stores.navBarStore));
-
-export default Stores;
diff --git a/app/app.js b/app/app.js
index 6c39160..ed6d994 100644
--- a/app/app.js
+++ b/app/app.js
@@ -38,41 +38,41 @@
// I haven't wrapped these dispatch methods in a action
// because I don't want to require fauxton/actions in this method.
addHeaderLink: function (link) {
- FauxtonAPI.dispatch({
+ FauxtonAPI.reduxDispatch({
type: 'ADD_NAVBAR_LINK',
link: link
});
},
updateHeaderLink: function (link) {
- FauxtonAPI.dispatch({
+ FauxtonAPI.reduxDispatch({
type: 'UPDATE_NAVBAR_LINK',
link: link
});
},
removeHeaderLink: function (link) {
- FauxtonAPI.dispatch({
+ FauxtonAPI.reduxDispatch({
type: 'REMOVE_NAVBAR_LINK',
link: link
});
},
hideLogin: function () {
- FauxtonAPI.dispatch({
+ FauxtonAPI.reduxDispatch({
type: 'NAVBAR_SHOW_HIDE_LOGIN_LOGOUT_SECTION',
visible: false
});
},
showLogout: function () {
- FauxtonAPI.dispatch({
+ FauxtonAPI.reduxDispatch({
type: 'NAVBAR_SHOW_LOGOUT_BUTTON'
});
},
showLogin: function () {
- FauxtonAPI.dispatch({
+ FauxtonAPI.reduxDispatch({
type: 'NAVBAR_SHOW_LOGIN_BUTTON'
});
}
diff --git a/app/core/base.js b/app/core/base.js
index 0762f7a..2278d84 100644
--- a/app/core/base.js
+++ b/app/core/base.js
@@ -24,19 +24,19 @@
// I haven't wrapped these dispatch methods in a action
// because I don't want to require fauxton/actions in this method.
addHeaderLink: function (link) {
- FauxtonAPI.dispatch({
+ FauxtonAPI.reduxDispatch({
type: 'ADD_NAVBAR_LINK',
link: link
});
},
showHeaderLinkBadge: function (link) {
- FauxtonAPI.dispatch({
+ FauxtonAPI.reduxDispatch({
type: 'SHOW_NAVBAR_LINK_BADGE',
link: link
});
},
hideHeaderLinkBadge: function (link) {
- FauxtonAPI.dispatch({
+ FauxtonAPI.reduxDispatch({
type: 'HIDE_NAVBAR_LINK_BADGE',
link: link
});