diff --git a/config/jest/setupTests.js b/config/jest/setupTests.js index 1040ec72c3..9fe4a22e72 100644 --- a/config/jest/setupTests.js +++ b/config/jest/setupTests.js @@ -24,7 +24,10 @@ Object.values = (obj) => Object.keys(obj).map(key => obj[key]) // Avoid jest error: "Error: Not implemented: navigation (except hash changes)" global.window.location.assign = () => {}; -// mock sessionStorage - feature flags config beforeEach(() => { + // mock sessionStorage - feature flags config sessionStorage.setItem('config', JSON.stringify(config)); + + // mock querySelector + global.document.querySelector = () => ({ offsetParent: '50px', scrollIntoView: () => {} }); }); diff --git a/package.json b/package.json index a9d2544328..ac13be2f9e 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,6 @@ "rc-steps": "^3.1.0", "react": "^15.6.2", "react-autosuggest": "^9.3.2", - "react-csv": "^1.1.1", "react-dom": "^15.6.2", "react-fontawesome": "^1.6.1", "react-helmet": "^5.2.0", @@ -141,7 +140,8 @@ "!src/Components/**/index.js", "!src/Components/StaticDevContent/*", "!src/actions/showStaticContent.js", - "!src/reducers/showStaticContent.js" + "!src/reducers/showStaticContent.js", + "!src/Components/CSV/**/*.{js,jsx}" ], "coverageReporters": [ "text-summary", diff --git a/public/config/config.json b/public/config/config.json index cfc70ec4ff..11c4d9bf78 100644 --- a/public/config/config.json +++ b/public/config/config.json @@ -5,6 +5,7 @@ "flags": { "bidding": true, "projected_vacancy": true, - "static_content": true + "static_content": true, + "notifications": true } } diff --git a/public/config/config_dev.json b/public/config/config_dev.json index cf69ab72e1..2f499285ff 100644 --- a/public/config/config_dev.json +++ b/public/config/config_dev.json @@ -3,8 +3,9 @@ "baseURL": "https://dev.talentmap.api.metaphasedev.com/api/v1" }, "flags": { - "bidding": false, + "bidding": true, "projected_vacancy": true, - "static_content": true + "static_content": true, + "notifications": true } } diff --git a/public/login.html b/public/login.html index adffd8197a..ae963cd454 100644 --- a/public/login.html +++ b/public/login.html @@ -15,16 +15,16 @@

Personas

Administrator
  • - Leah Shadtrach + Leah Shadtrach (CDO)
  • - Jenny Townpost + Jenny Townpost (Bidder)
  • - Tarek Rehman + Tarek Rehman (Bidder)
  • - Wendy Woodward + Wendy Woodward (AO)
  • diff --git a/src/Components/BidTracker/BidTrackerCardContainer/__snapshots__/BidTrackerCardContainer.test.jsx.snap b/src/Components/BidTracker/BidTrackerCardContainer/__snapshots__/BidTrackerCardContainer.test.jsx.snap index 272a003e43..4d1494ec47 100644 --- a/src/Components/BidTracker/BidTrackerCardContainer/__snapshots__/BidTrackerCardContainer.test.jsx.snap +++ b/src/Components/BidTracker/BidTrackerCardContainer/__snapshots__/BidTrackerCardContainer.test.jsx.snap @@ -87,6 +87,14 @@ exports[`BidTrackerCardContainerComponent matches snapshot when priorityExists i "representation": "[00180000] OMS (DCM) (Addis Ababa, Ethiopia)", }, ], + "favorite_positions_pv": Array [ + Object { + "id": 10, + }, + Object { + "id": 20, + }, + ], "grade": "03", "id": 1, "initials": "JD", @@ -267,6 +275,14 @@ exports[`BidTrackerCardContainerComponent matches snapshot when priorityExists i "representation": "[00180000] OMS (DCM) (Addis Ababa, Ethiopia)", }, ], + "favorite_positions_pv": Array [ + Object { + "id": 10, + }, + Object { + "id": 20, + }, + ], "grade": "03", "id": 1, "initials": "JD", diff --git a/src/Components/BidTracker/BidTrackerCardList/__snapshots__/BidTrackerCardList.test.jsx.snap b/src/Components/BidTracker/BidTrackerCardList/__snapshots__/BidTrackerCardList.test.jsx.snap index 1e33bf1ef9..99065bda09 100644 --- a/src/Components/BidTracker/BidTrackerCardList/__snapshots__/BidTrackerCardList.test.jsx.snap +++ b/src/Components/BidTracker/BidTrackerCardList/__snapshots__/BidTrackerCardList.test.jsx.snap @@ -86,6 +86,14 @@ exports[`BidTrackerCardListComponent matches snapshot 1`] = ` "representation": "[00180000] OMS (DCM) (Addis Ababa, Ethiopia)", }, ], + "favorite_positions_pv": Array [ + Object { + "id": 10, + }, + Object { + "id": 20, + }, + ], "grade": "03", "id": 1, "initials": "JD", @@ -196,6 +204,14 @@ exports[`BidTrackerCardListComponent matches snapshot 1`] = ` "representation": "[00180000] OMS (DCM) (Addis Ababa, Ethiopia)", }, ], + "favorite_positions_pv": Array [ + Object { + "id": 10, + }, + Object { + "id": 20, + }, + ], "grade": "03", "id": 1, "initials": "JD", @@ -306,6 +322,14 @@ exports[`BidTrackerCardListComponent matches snapshot 1`] = ` "representation": "[00180000] OMS (DCM) (Addis Ababa, Ethiopia)", }, ], + "favorite_positions_pv": Array [ + Object { + "id": 10, + }, + Object { + "id": 20, + }, + ], "grade": "03", "id": 1, "initials": "JD", diff --git a/src/Components/BidTracker/__snapshots__/BidTracker.test.jsx.snap b/src/Components/BidTracker/__snapshots__/BidTracker.test.jsx.snap index 4b4e7a9242..1f936ec3de 100644 --- a/src/Components/BidTracker/__snapshots__/BidTracker.test.jsx.snap +++ b/src/Components/BidTracker/__snapshots__/BidTracker.test.jsx.snap @@ -268,6 +268,14 @@ exports[`BidTrackerComponent matches snapshot 1`] = ` "representation": "[00180000] OMS (DCM) (Addis Ababa, Ethiopia)", }, ], + "favorite_positions_pv": Array [ + Object { + "id": 10, + }, + Object { + "id": 20, + }, + ], "grade": "03", "id": 1, "initials": "JD", diff --git a/src/Components/BidderPortfolio/BidderPortfolioCard/__snapshots__/BidderPortfolioCard.test.jsx.snap b/src/Components/BidderPortfolio/BidderPortfolioCard/__snapshots__/BidderPortfolioCard.test.jsx.snap index 3f212b6a1b..e210ff8951 100644 --- a/src/Components/BidderPortfolio/BidderPortfolioCard/__snapshots__/BidderPortfolioCard.test.jsx.snap +++ b/src/Components/BidderPortfolio/BidderPortfolioCard/__snapshots__/BidderPortfolioCard.test.jsx.snap @@ -41,6 +41,14 @@ exports[`BidderPortfolioCardComponent matches snapshot 1`] = ` "representation": "[00180000] OMS (DCM) (Addis Ababa, Ethiopia)", }, ], + "favorite_positions_pv": Array [ + Object { + "id": 10, + }, + Object { + "id": 20, + }, + ], "grade": "03", "id": 1, "initials": "JD", @@ -96,6 +104,14 @@ exports[`BidderPortfolioCardComponent matches snapshot 1`] = ` "representation": "[00180000] OMS (DCM) (Addis Ababa, Ethiopia)", }, ], + "favorite_positions_pv": Array [ + Object { + "id": 10, + }, + Object { + "id": 20, + }, + ], "grade": "03", "id": 1, "initials": "JD", diff --git a/src/Components/BidderPortfolio/BidderPortfolioContainer/BidderPortfolioContainer.jsx b/src/Components/BidderPortfolio/BidderPortfolioContainer/BidderPortfolioContainer.jsx index 897f868f40..35a6d299ca 100644 --- a/src/Components/BidderPortfolio/BidderPortfolioContainer/BidderPortfolioContainer.jsx +++ b/src/Components/BidderPortfolio/BidderPortfolioContainer/BidderPortfolioContainer.jsx @@ -1,26 +1,30 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { BIDDER_LIST } from '../../../Constants/PropTypes'; -import { scrollToTop } from '../../../utilities'; +import { scrollToId } from '../../../utilities'; import BidderPortfolioCardList from '../BidderPortfolioCardList'; import BidderPortfolioGridList from '../BidderPortfolioGridList'; import PaginationWrapper from '../../PaginationWrapper/PaginationWrapper'; import Alert from '../../Alert/Alert'; +const ID = 'bidder-portfolio-container'; + class BidderPortfolioContainer extends Component { constructor(props) { super(props); this.onPageChange = this.onPageChange.bind(this); } onPageChange(q) { - scrollToTop(); - this.props.queryParamUpdate(q); + scrollToId({ el: '.bidder-portfolio-container', config: { duration: 400 } }); + setTimeout(() => { + this.props.queryParamUpdate(q); + }, 600); } render() { const { bidderPortfolio, pageSize, pageNumber, showListView, showEdit } = this.props; const noResults = bidderPortfolio.results.length === 0; return ( -
    +
    { showListView ? diff --git a/src/Components/BidderPortfolio/BidderPortfolioContainer/BidderPortfolioContainer.test.jsx b/src/Components/BidderPortfolio/BidderPortfolioContainer/BidderPortfolioContainer.test.jsx index be59d58c8d..48af52d98f 100644 --- a/src/Components/BidderPortfolio/BidderPortfolioContainer/BidderPortfolioContainer.test.jsx +++ b/src/Components/BidderPortfolio/BidderPortfolioContainer/BidderPortfolioContainer.test.jsx @@ -16,7 +16,7 @@ describe('BidderPortfolioContainerComponent', () => { expect(wrapper).toBeDefined(); }); - it('can call the onPageChange function', () => { + it('can call the onPageChange function', (done) => { const spy = sinon.spy(); const wrapper = shallow( { queryParamUpdate={spy} />); wrapper.instance().onPageChange({}); - sinon.assert.calledOnce(spy); + setTimeout(() => { + sinon.assert.calledOnce(spy); + done(); + }, 700); }); it('matches snapshot when the all property is greater than zero', () => { diff --git a/src/Components/BidderPortfolio/BidderPortfolioContainer/__snapshots__/BidderPortfolioContainer.test.jsx.snap b/src/Components/BidderPortfolio/BidderPortfolioContainer/__snapshots__/BidderPortfolioContainer.test.jsx.snap index 89adc6ecdf..d44df7f7c4 100644 --- a/src/Components/BidderPortfolio/BidderPortfolioContainer/__snapshots__/BidderPortfolioContainer.test.jsx.snap +++ b/src/Components/BidderPortfolio/BidderPortfolioContainer/__snapshots__/BidderPortfolioContainer.test.jsx.snap @@ -3,6 +3,7 @@ exports[`BidderPortfolioContainerComponent matches snapshot when the all property is greater than zero 1`] = `
    ( data.map(entry => ({ - ...entry, + ...mapValues(entry, x => !x ? '' : x), // eslint-disable-line no-confusing-arrow + grade: getFormattedNumCSV(entry.grade), // any other processing we may want to do here })) ); @@ -74,7 +76,16 @@ export class ExportLink extends Component { return (
    - +
    ); } diff --git a/src/Components/BidderPortfolio/ExportLink/__snapshots__/ExportLink.test.jsx.snap b/src/Components/BidderPortfolio/ExportLink/__snapshots__/ExportLink.test.jsx.snap index d342c36bfe..6c63ed7e4e 100644 --- a/src/Components/BidderPortfolio/ExportLink/__snapshots__/ExportLink.test.jsx.snap +++ b/src/Components/BidderPortfolio/ExportLink/__snapshots__/ExportLink.test.jsx.snap @@ -57,7 +57,8 @@ exports[`SearchResultsExportLink matches snapshot 1`] = ` separator="," tabIndex="-1" target="_blank" - uFEFF={true} + transform={[Function]} + uFEFF={false} />
    `; diff --git a/src/Components/CSV/components/Download.js b/src/Components/CSV/components/Download.js new file mode 100644 index 0000000000..fbe1ec301b --- /dev/null +++ b/src/Components/CSV/components/Download.js @@ -0,0 +1,50 @@ +/* eslint-disable */ +import React from 'react'; +import {buildURI} from '../core'; +import { + defaultProps as commonDefaultProps, + propTypes as commonPropTypes} from '../metaProps'; +const defaultProps = { + target: '_blank' +}; + +/** + * + * @example ../../sample-site/csvdownload.example.md + */ +class CSVDownload extends React.Component { + + static defaultProps = Object.assign( + commonDefaultProps, + defaultProps + ); + + static propTypes = commonPropTypes; + + constructor(props) { + super(props); + this.state={}; + } + + buildURI() { + return buildURI(...arguments); + } + + componentDidMount(){ + const {data, headers, separator, enclosingCharacter, uFEFF, target, specs, replace, transform} = this.props; + this.state.page = window.open( + this.buildURI(data, uFEFF, headers, separator, enclosingCharacter), target, specs, replace, transform + ); + } + + getWindow() { + return this.state.page; + } + + render(){ + return (null) + } +} + +export default CSVDownload; +/* eslint-enable */ diff --git a/src/Components/CSV/components/Link.js b/src/Components/CSV/components/Link.js new file mode 100644 index 0000000000..1d94f2c152 --- /dev/null +++ b/src/Components/CSV/components/Link.js @@ -0,0 +1,116 @@ +/* eslint-disable */ +import React from 'react'; +import { buildURI, toCSV } from '../core'; +import { + defaultProps as commonDefaultProps, + propTypes as commonPropTypes +} from '../metaProps'; + +/** + * + * @example ../../sample-site/csvlink.example.md + */ +class CSVLink extends React.Component { + static defaultProps = commonDefaultProps; + static propTypes = commonPropTypes; + + constructor(props) { + super(props); + this.buildURI = this.buildURI.bind(this); + this.state = { href: '' }; + } + + componentDidMount() { + const {data, headers, separator, uFEFF, enclosingCharacter, transform} = this.props; + this.setState({ href: this.buildURI(data, uFEFF, headers, separator, enclosingCharacter, transform) }); + } + + componentWillReceiveProps(nextProps) { + const { data, headers, separator, transform, uFEFF, enclosingCharacter } = nextProps; + this.setState({ href: this.buildURI(data, uFEFF, headers, separator, enclosingCharacter, transform) }); + } + + buildURI() { + return buildURI(...arguments); + } + + /** + * In IE11 this method will trigger the file download + */ + handleLegacy(event, data, headers, separator, filename, enclosingCharacter, transform) { + // If this browser is IE 11, it does not support the `download` attribute + if (window.navigator.msSaveOrOpenBlob) { + // Stop the click propagation + event.preventDefault(); + + let blob = new Blob([toCSV(data, headers, separator, enclosingCharacter, transform)]); + window.navigator.msSaveBlob(blob, filename); + + return false; + } + } + + handleAsyncClick(event, ...args) { + const done = proceed => { + if (proceed === false) { + event.preventDefault(); + return; + } + this.handleLegacy(event, ...args); + }; + + this.props.onClick(event, done); + } + + handleSyncClick(event, ...args) { + const stopEvent = this.props.onClick(event) === false; + if (stopEvent) { + event.preventDefault(); + return; + } + this.handleLegacy(event, ...args); + } + + handleClick(...args) { + return event => { + if (typeof this.props.onClick === 'function') { + return this.props.asyncOnClick + ? this.handleAsyncClick(event, ...args) + : this.handleSyncClick(event, ...args); + } + this.handleLegacy(event, ...args); + }; + } + + render() { + const { + data, + headers, + separator, + filename, + uFEFF, + children, + onClick, + asyncOnClick, + enclosingCharacter, + transform, + ...rest + } = this.props; + + return ( + (this.link = link)} + target="_self" + href={this.state.href} + onClick={this.handleClick(data, headers, separator, filename, enclosingCharacter, transform)} + > + {children} + + ); + } +} + +export default CSVLink; +/* eslint-enable */ diff --git a/src/Components/CSV/core.js b/src/Components/CSV/core.js new file mode 100644 index 0000000000..6b76d9533a --- /dev/null +++ b/src/Components/CSV/core.js @@ -0,0 +1,98 @@ +/* eslint-disable */ +/** + * Simple safari detection based on user agent test + */ +export const isSafari = () => /^((?!chrome|android).)*safari/i.test(navigator.userAgent); + +const returnVal = v => v; + +export const isJsons = ((array) => Array.isArray(array) && array.every( + row => (typeof row === 'object' && !(row instanceof Array)) +)); + +export const isArrays = ((array) => Array.isArray(array) && array.every( + row => Array.isArray(row) +)); + +export const jsonsHeaders = ((array) => Array.from( + array.map(json => Object.keys(json)) + .reduce((a, b) => new Set([...a, ...b]), []) +)); + +export const jsons2arrays = (jsons, headers) => { + headers = headers || jsonsHeaders(jsons); + + // allow headers to have custom labels, defaulting to having the header data key be the label + let headerLabels = headers; + let headerKeys = headers; + if (isJsons(headers)) { + headerLabels = headers.map((header) => header.label); + headerKeys = headers.map((header) => header.key); + } + + const data = jsons.map((object) => headerKeys.map((header) => getHeaderValue(header, object))); + return [headerLabels, ...data]; +}; + +export const getHeaderValue = (property, obj) => { + const foundValue = property + .replace(/\[([^\]]+)]/g, ".$1") + .split(".") + .reduce(function(o, p, i, arr) { + // if at any point the nested keys passed do not exist, splice the array so it doesnt keep reducing + if (o[p] === undefined) { + arr.splice(1); + } else { + return o[p]; + } + }, obj); + + return (foundValue === undefined) ? '' : foundValue; +} + +export const elementOrEmpty = (element) => element || element === 0 ? element : ''; + +export const joiner = ((data, separator = ',', enclosingCharacter = '"', transform = returnVal) => { + return data + .filter(e => e) + .map( + row => row + .map((element) => elementOrEmpty(element)) + .map(column => transform(`${enclosingCharacter}${column}${enclosingCharacter}`)) + .join(separator) + ) + .join(`\n`); +}); + +export const arrays2csv = ((data, headers, separator, enclosingCharacter, transform = returnVal) => + joiner(headers ? [headers, ...data] : data, separator, enclosingCharacter, transform) +); + +export const jsons2csv = ((data, headers, separator, enclosingCharacter, transform = returnVal) => + joiner(jsons2arrays(data, headers), separator, enclosingCharacter, transform) +); + +export const string2csv = ((data, headers, separator, enclosingCharacter) => + (headers) ? `${headers.join(separator)}\n${data}`: data +); + +export const toCSV = (data, headers, separator, enclosingCharacter, transform = returnVal) => { + if (isJsons(data)) return jsons2csv(data, headers, separator, enclosingCharacter, transform); + if (isArrays(data)) return arrays2csv(data, headers, separator, enclosingCharacter, transform); + if (typeof data ==='string') return string2csv(data, headers, separator); + throw new TypeError(`Data should be a "String", "Array of arrays" OR "Array of objects" `); +}; + +export const buildURI = ((data, uFEFF, headers, separator, enclosingCharacter, transform = returnVal) => { + const csv = toCSV(data, headers, separator, enclosingCharacter, transform); + const type = isSafari() ? 'application/csv' : 'text/csv'; + const blob = new Blob([uFEFF ? '\uFEFF' : '', csv], {type}); + const dataURI = `data:${type};charset=utf-8,${uFEFF ? '\uFEFF' : ''}${csv}`; + + const URL = window.URL || window.webkitURL; + + return (typeof URL.createObjectURL === 'undefined') + ? dataURI + : URL.createObjectURL(blob); +}); +/* eslint-enable */ diff --git a/src/Components/CSV/index.js b/src/Components/CSV/index.js new file mode 100644 index 0000000000..4dd8149c44 --- /dev/null +++ b/src/Components/CSV/index.js @@ -0,0 +1,5 @@ +import Download from './components/Download'; +import Link from './components/Link'; + +export const CSVDownload = Download; +export const CSVLink = Link; diff --git a/src/Components/CSV/metaProps.js b/src/Components/CSV/metaProps.js new file mode 100644 index 0000000000..93d92ae157 --- /dev/null +++ b/src/Components/CSV/metaProps.js @@ -0,0 +1,39 @@ +/* eslint-disable */ +import React from 'react'; +import { string, array, oneOfType, bool, func } from 'prop-types'; + + +export const propTypes = { + data: oneOfType([string, array]).isRequired, + headers: array, + target: string, + separator: string, + filename: string, + uFEFF: bool, + onClick: func, + asyncOnClick: bool, + transform: func +}; + +export const defaultProps = { + separator: ',', + filename: 'generatedBy_react-csv.csv', + uFEFF: true, + asyncOnClick: false, + transform: v => v, +}; + +export const PropsNotForwarded = [ + `data`, + `headers` +]; + +// export const DownloadPropTypes = Object.assign( +// {}, +// PropTypes, +// { +// : , +// } +// ); + +/* eslint-enable */ diff --git a/src/Components/CSV/readme.md b/src/Components/CSV/readme.md new file mode 100644 index 0000000000..ed9df32a38 --- /dev/null +++ b/src/Components/CSV/readme.md @@ -0,0 +1,3 @@ +Fork of react-csv + +Waiting for https://github.com/react-csv/react-csv/pull/155 to be approved and merged. Once done, revert to the node module and delete this folder. diff --git a/src/Components/CompareList/CompareList.jsx b/src/Components/CompareList/CompareList.jsx index b1237663a0..5546ffb988 100644 --- a/src/Components/CompareList/CompareList.jsx +++ b/src/Components/CompareList/CompareList.jsx @@ -286,7 +286,7 @@ class CompareList extends Component { ( -
    -
    -
    - -
    -
    - +
    +
    + +
    +
    +
    -
    - { - favoritePositionsIsLoading && !favoritePositionsHasErrored && - - } - { - !favoritePositionsIsLoading && !favorites.results.length && - - } - -
    -); + ); + } +} FavoritePositions.propTypes = { - favorites: POSITION_SEARCH_RESULTS.isRequired, + favorites: FAVORITE_POSITIONS_ARRAY, + favoritesPV: FAVORITE_POSITIONS_ARRAY, favoritePositionsIsLoading: PropTypes.bool.isRequired, favoritePositionsHasErrored: PropTypes.bool.isRequired, bidList: BID_RESULTS.isRequired, onSortChange: PropTypes.func.isRequired, }; +FavoritePositions.defaultProps = { + favorites: [], + favoritesPV: [], +}; + export default FavoritePositions; diff --git a/src/Components/FavoritePositions/FavoritePositions.test.jsx b/src/Components/FavoritePositions/FavoritePositions.test.jsx index c901af9fe8..a8008be590 100644 --- a/src/Components/FavoritePositions/FavoritePositions.test.jsx +++ b/src/Components/FavoritePositions/FavoritePositions.test.jsx @@ -7,7 +7,8 @@ import bidListObject from '../../__mocks__/bidListObject'; describe('FavoritePositionsComponent', () => { const props = { - favorites: resultsObject, + favorites: resultsObject.results, + favoritesPV: resultsObject.results, toggleFavoritePositionIsLoading: false, toggleFavoritePositionHasErrored: false, favoritePositionsIsLoading: false, @@ -24,31 +25,41 @@ describe('FavoritePositionsComponent', () => { expect(wrapper).toBeDefined(); }); + it('is defined when selected === open', () => { + const wrapper = shallow( + , + ); + wrapper.setState({ selected: 'open' }); + expect(wrapper).toBeDefined(); + }); + + it('is defined when selected === pv', () => { + const wrapper = shallow( + , + ); + wrapper.setState({ selected: 'pv' }); + expect(wrapper).toBeDefined(); + }); + it('can receive props', () => { const wrapper = shallow( , ); - expect(wrapper.instance().props.favorites).toBe(resultsObject); + expect(wrapper.instance().props.favorites).toBe(props.favorites); }); it('displays an alert if there are no positions', () => { const wrapper = shallow( , ); expect(wrapper.find('NoFavorites').exists()).toBe(true); }); - it('renders the Spinner when loading', () => { - const wrapper = shallow( - , - ); - expect(wrapper.instance().props.favorites).toBe(resultsObject); - }); - it('renders the Spinner when loading', () => { const wrapper = shallow( onClick(selected)); + } + render() { + const { denominator, options } = this.props; + return ( +
    + {options.map((m) => { + const isActive = m.value === this.state.selected; + return ( + + ); + })} +
    + ); + } +} + +Nav.propTypes = { + options: PropTypes.arrayOf( + PropTypes.shape({ + title: PropTypes.string.isRequired, + numerator: PropTypes.number.isRequired, + }), + ), + denominator: PropTypes.number.isRequired, + selected: PropTypes.string, + onClick: PropTypes.func, +}; + +Nav.defaultProps = { + options: [], + denominator: 1, + selected: '', + onClick: EMPTY_FUNCTION, +}; + +export default Nav; diff --git a/src/Components/FavoritePositions/Nav/Nav.test.jsx b/src/Components/FavoritePositions/Nav/Nav.test.jsx new file mode 100644 index 0000000000..2bebe5a524 --- /dev/null +++ b/src/Components/FavoritePositions/Nav/Nav.test.jsx @@ -0,0 +1,57 @@ +import { shallow } from 'enzyme'; +import React from 'react'; +import toJSON from 'enzyme-to-json'; +import sinon from 'sinon'; +import Nav from './Nav'; + +describe('NavComponent', () => { + const props = { + options: [ + { title: 'Test', value: '1', numerator: 2 }, + { title: 'Test 2', value: '2', numerator: 3 }, + ], + denominator: 5, + }; + + it('is defined', () => { + const wrapper = shallow( +
    +
    +