Skip to content
This repository has been archived by the owner on Oct 11, 2023. It is now read-only.

Features/settings page #133

Merged
merged 2 commits into from
Apr 12, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/actions/signalRActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,11 @@ export const connectionClosed = (error, stack) => ({
export const filterTypes = {
none: {
value: 'none',
description: 'Do not filter events sent from the Gateway'
description: 'Receive Notifications For All Events For All Incidents'
},
sync: {
value: 'sync',
description: 'Receive only events that match the event filter on the current page'
description: 'Receive Notifications Only For Events Matching The Current Incident and Filter'
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/components/Debug.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export const Debug = () => {
<FlatButtonStyled
label='Clear Auth Cache'
onTouchTap={() => clearCache()}
/>
/>
</div>)
}

Expand Down
2 changes: 2 additions & 0 deletions src/components/MainComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import Debug from 'components/Debug'
import UncorrelatedEvents from 'components/Timeline/UncorrelatedEvents'
import { isChromeExtensionBackground } from 'services/notificationService'
import Notifications from 'components/Extension/Notifications'
import Preferences from 'components/TopNav/Preferences'

const history = createBrowserHistory()

Expand All @@ -37,6 +38,7 @@ export default class MainComponent extends React.Component {
<Route path='/tickets/:ticketId' component={Ticket} />
<Route path='/incidents/:incidentId' component={incidentRedirect} />
<Route path='/debug' render={() => <Debug />} />
<Route path='/preferences' component={Preferences} />
<Route path='/events' component={UncorrelatedEvents} />
</div>
</Router>
Expand Down
38 changes: 17 additions & 21 deletions src/components/TopNav/NavMenu.js
Original file line number Diff line number Diff line change
@@ -1,47 +1,45 @@
import React from 'react'
import { Link, withRouter } from 'react-router-dom'
import { withRouter } from 'react-router-dom'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import MenuItem from 'material-ui/MenuItem'
import Divider from 'material-ui/Divider'
import IconMenu from 'material-ui/IconMenu'
import NavigationMenu from 'material-ui/svg-icons/navigation/menu'
import IconButton from 'material-ui/IconButton'
import ArrowDropRight from 'material-ui/svg-icons/navigation-arrow-drop-right'
import ActionDeleteForever from 'material-ui/svg-icons/action/delete-forever'
import ActionDelete from 'material-ui/svg-icons/action/delete'

import MenuLink from 'components/elements/MenuLink'
import { removeTicketFromRecent, removeAllTicketsFromRecent } from 'actions/ticketActions'
import * as auth from 'services/authNService'
import Preferences from 'components/TopNav/Preferences'

const clearRecentTickets = (dispatch) =>
<MenuItem key='clear' primaryText={'Clear Recent Tickets'} onClick={() => dispatch(removeAllTicketsFromRecent())}
rightIcon={<ActionDeleteForever onClick={() => dispatch(removeAllTicketsFromRecent())}
/>}
/>

export const NavMenu = ({ dispatch, history, ticketIds, eventFilter, currentEventFilterType }) => {
export const NavMenu = ({
dispatch,
history,
ticketIds
}) => {
return (<IconMenu
iconButtonElement={<IconButton><NavigationMenu /></IconButton>}
anchorOrigin={{horizontal: 'left', vertical: 'bottom'}}
targetOrigin={{horizontal: 'left', vertical: 'top'}}
>
<MenuItem containerElement={<Link to='/search' />} primaryText='Incident Search' />
<MenuItem containerElement={<Link to='/events' />} primaryText='Events for All Incidents' />
{MenuLink('Incident Search', '/search')}
{MenuLink('Events for All Incidents', '/events')}
<Divider />
<MenuItem
key='preferences'
primaryText={'Preferences'}
rightIcon={<ArrowDropRight />}
menuItems={Preferences(eventFilter, currentEventFilterType, dispatch)}
/>
{MenuLink('Preferences', '/preferences')}
<Divider />
{ clearRecentTickets(dispatch) }
{ticketIds && ticketIds.map(id => MenuLink('ticket', id, removeTicketFromRecent, dispatch)) }
{clearRecentTickets(dispatch)}
{ticketIds && ticketIds.map(id => MenuLink(`Ticket ${id}`, `/tickets/${id}`, <ActionDelete onClick={() => dispatch(removeTicketFromRecent(id))} />)) }
<Divider />
<MenuItem onClick={() => dispatch(auth.logOut)} primaryText='LogOut' />
<MenuItem containerElement={<Link to='/debug' />} primaryText='Debug' />
{MenuLink('Debug', '/debug')}
</IconMenu>)
}

Expand All @@ -51,14 +49,12 @@ NavMenu.propTypes = {
}

export const mapStateToProps = (state, ownProps) => {
var pathname = ownProps.location.pathname
var currentId = /\d/.test(pathname) && pathname.match(/(\d+)/)[1]
let idContainsANumberAndIsNotCurrent = (id) => id !== currentId && /\d/.test(id)
const pathname = ownProps.location.pathname
const currentId = /\d/.test(pathname) && pathname.match(/(\d+)/)[1]
const idContainsANumberAndIsNotCurrent = (id) => id !== currentId && /\d/.test(id)

return {
...ownProps,
eventFilter: state.events.filter,
currentEventFilterType: state.signalR.filterPreferences.eventFilterType,
history: ownProps.history,
ticketIds: Object.entries(state.tickets.map)
.filter(kvp => kvp[1] !== null)
.map(kvp => kvp[0])
Expand Down
56 changes: 39 additions & 17 deletions src/components/TopNav/Preferences.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,45 @@
import React from 'react'
import MenuItem from 'material-ui/MenuItem'
import { connect } from 'react-redux'
import { RadioButton, RadioButtonGroup } from 'material-ui/RadioButton'

import * as signalRActions from 'actions/signalRActions'

export const Preferences = (eventFilter, currentEventFilterType, dispatch) => [
<MenuItem
key='signalREventFilter'
primaryText={'Event Filter Preferences:'}
/>,
...Object.values(signalRActions.filterTypes).map(filterType => <MenuItem
key={filterType.value}
checked={currentEventFilterType === filterType.value}
primaryText={
<div
onClick={() => dispatch(signalRActions.updateEventFilterPreference(filterType.value, eventFilter))}
>
"{filterType.value}": {filterType.description}
</div>}
/>)
]
export const EventFilterPreferences = ({
currentEventFilterObject,
currentEventFilterPreference,
dispatch
}) => <div style={{ padding: '16px' }}>
<h3 key='signalREventFilter'>Event Filter Preferences:</h3>
<RadioButtonGroup
name='Event Filter Preferences'
onChange={SelectEventFilterPreference(dispatch, currentEventFilterObject)}
defaultSelected={currentEventFilterPreference}
>
{Object.values(signalRActions.filterTypes)
.map(filterType =>
<RadioButton
style={{ padding: '8px' }}
key={filterType.value}
value={filterType.value}
label={filterType.description}
/>
)
}
</RadioButtonGroup>
</div>

export const mapStateToEventFilterPreferencesProps = (state) => ({
currentEventFilterObject: state.events.filter,
currentEventFilterPreference: state.signalR.filterPreferences.eventFilterType
})

export const ConnectedEventFilterPreferences = connect(mapStateToEventFilterPreferencesProps)(EventFilterPreferences)

export const SelectEventFilterPreference = (dispatch, currentEventFilter) =>
(event, value) => dispatch(signalRActions.updateEventFilterPreference(value, currentEventFilter))

export const Preferences = () => <div>
<ConnectedEventFilterPreferences />
</div>

export default Preferences
27 changes: 13 additions & 14 deletions src/components/elements/MenuLink.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,22 @@ import React from 'react'
import PropTypes from 'prop-types'
import { Link } from 'react-router-dom'
import MenuItem from 'material-ui/MenuItem'
import ActionDelete from 'material-ui/svg-icons/action/delete'

export const MenuLink = (type, id, onClick, dispatch) => {
var typeRoute = `/${type}s/${id}`
return (
<MenuItem
key={`${type}-${id}`}
containerElement={<Link to={typeRoute} />}
primaryText={`${type} ${id}`}
rightIcon={<ActionDelete onClick={() => dispatch(onClick(id))} />}
/>
)
}
export const MenuLink = (
primaryText,
route,
rightIcon
) => <MenuItem
key={primaryText + route}
containerElement={<Link to={route} />}
primaryText={primaryText}
rightIcon={rightIcon}
/>

MenuLink.propTypes = {
type: PropTypes.string.isRequired,
id: PropTypes.number.isRequired
primaryText: PropTypes.string.isRequired,
route: PropTypes.string.isRequired,
rightIcon: PropTypes.object
}

export default MenuLink
81 changes: 45 additions & 36 deletions test/components/TopNav/NavMenuTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,58 +2,64 @@
import { expect } from 'chai'
import React from 'react'
import { shallow } from 'enzyme'
require('test/helpers/configureEnzyme')
import { NavMenu, mapStateToProps } from 'components/TopNav/NavMenu'
import NotificationsNone from 'material-ui/svg-icons/social/notifications-none'
import IconButton from 'material-ui/IconButton'
import MenuItem from 'material-ui/MenuItem'
import IconMenu from 'material-ui/IconMenu'
import NavigationMenu from 'material-ui/svg-icons/navigation/menu'
import { Link } from 'react-router-dom'
require('test/helpers/configureEnzyme')

function mockDispatch (object) { }
const mockDispatch = () => null

function setup() {
return shallow(<NavMenu dispatch={mockDispatch} ticketIds={['1111', '2222', '3333']}/>)
}
const setup = () => shallow(
<NavMenu
dispatch={mockDispatch}
ticketIds={['1111', '2222', '3333']}
/>
)

describe('NavMenu', function test () {
beforeEach( () => {
this.wrapper = setup()
const testObject = setup()

const expectMenuLink = (testObject, expectedPrimaryText, expectedLink) => {
expect(testObject.type).to.equal(MenuItem)
expect(testObject.props.primaryText).to.equal(expectedPrimaryText)
expect(testObject.props.containerElement.type).to.equal(Link)
expect(testObject.props.containerElement.props.to).to.equal(expectedLink)
}

it('Should render an IconMenu with an icon button', function () {
expect(testObject.type()).to.equal(IconMenu)
expect(testObject.props().iconButtonElement.type).to.equal(IconButton)
})

it('Should render an IconMenu with an icon button', () => {
expect(this.wrapper.type()).to.equal(IconMenu)
expect(this.wrapper.props().iconButtonElement.type).to.equal(IconButton)
it('Should render an Incident Search link', function () {
const link = testObject.props().children[0]

expectMenuLink(link, 'Incident Search', '/search')
})

it('Should render an Incident Search link', () => {
let link = this.wrapper.props().children[0]
it('Should render an Events for All Incidents link', function () {
const link = testObject.props().children[1]

expect(link.type).to.equal(MenuItem)
expect(link.props.primaryText).to.equal('Incident Search')
expect(link.props.containerElement.type).to.equal(Link)
expect(link.props.containerElement.props.to).to.equal('/search')
expectMenuLink(link, 'Events for All Incidents', '/events')
})

it('Should render an Events for All Incidents link', () => {
let link = this.wrapper.props().children[1]
it('Should render a Preferences link', function () {
const link = testObject.props().children[3]

expect(link.type).to.equal(MenuItem)
expect(link.props.primaryText).to.equal('Events for All Incidents')
expect(link.props.containerElement.type).to.equal(Link)
expect(link.props.containerElement.props.to).to.equal('/events')
expectMenuLink(link, 'Preferences', '/preferences')
})

it('Should render a log out link', () => {
let link = this.wrapper.props().children[8]
it('Should render a log out link', function () {
const link = testObject.props().children[8]

expect(link.type).to.equal(MenuItem)
expect(link.props.primaryText).to.equal('LogOut')
})

it('Should render links to previously visited tickets', () => {
let links = this.wrapper.props().children[6]
it('Should render links to previously visited tickets', function () {
const links = testObject.props().children[6]

expect(links[0].props.containerElement.props.to).to.equal('/tickets/1111')
expect(links[1].props.containerElement.props.to).to.equal('/tickets/2222')
Expand All @@ -62,17 +68,17 @@ describe('NavMenu', function test () {
})

describe('mapStateToProps', function test () {
let state = {
const state = {
tickets: {
map: {
'12345': 'value',
'67890': 'unused'
'67890': 'unused'
}
},
events: {
filter: {
incidentId: 1,
eventTypes: [0,5]
eventTypes: [ 0, 5 ]
}
},
signalR: {
Expand All @@ -82,15 +88,18 @@ describe('mapStateToProps', function test () {
}
}

let ownProps = { location: { pathname: '/tickets/4444' } }
const ownProps = {
history: {},
location: { pathname: '/tickets/4444' }
}

beforeEach(() => { this.result = mapStateToProps(state, ownProps)})
const result = mapStateToProps(state, ownProps)

it('passes ownProps data through', () => {
expect(this.result).to.contain(ownProps)
it('passes history from ownProps', () => {
expect(result.history).to.equal(ownProps.history)
})

it('transforms the tickets.map into ticketIds', () => {
expect(this.result.ticketIds).to.contain('12345', '67890')
expect(result.ticketIds).to.contain('12345', '67890')
})
})