Skip to content
Snippets Groups Projects
Commit d4024a81 authored by Jack Reed's avatar Jack Reed Committed by Chris Beer
Browse files

Refactor SearchPanelControls to drop navigation into its own component

parent 186dee5e
No related branches found
No related tags found
No related merge requests found
......@@ -21,9 +21,10 @@ function createWrapper(props) {
}
describe('SearchPanelControls', () => {
it('renders a form', () => {
it('renders a form and navigation', () => {
const wrapper = createWrapper();
expect(wrapper.find('form').length).toEqual(1);
expect(wrapper.find('Connect(WithStyles(WithPlugins(SearchPanelNavigation)))').length).toEqual(1);
});
it('submits a search when an autocomplete suggestion is picked', () => {
const fetchSearch = jest.fn();
......@@ -114,28 +115,4 @@ describe('SearchPanelControls', () => {
expect(wrapper.find(Downshift).props().inputValue).toEqual('');
});
});
describe('when searchHits are available', () => {
it('renders text with buttons', () => {
const selectContentSearchAnnotation = jest.fn();
const wrapper = createWrapper({
searchHits: [{ annotations: ['1'] }, { annotations: ['2'] }, { annotations: ['3'] }],
selectContentSearchAnnotation,
selectedContentSearchAnnotation: ['2'],
});
expect(wrapper.find('WithStyles(ForwardRef(Typography))').text()).toEqual('searchPageSeparator');
expect(wrapper.find('Connect(WithPlugins(MiradorMenuButton))[disabled=false]').length).toEqual(2);
wrapper.find('Connect(WithPlugins(MiradorMenuButton))[disabled=false]').first().props().onClick();
expect(selectContentSearchAnnotation).toHaveBeenCalledWith('window', ['1']);
wrapper.find('Connect(WithPlugins(MiradorMenuButton))[disabled=false]').last().props().onClick();
expect(selectContentSearchAnnotation).toHaveBeenCalledWith('window', ['3']);
});
it('buttons disabled when no next/prev', () => {
const wrapper = createWrapper({
searchHits: [{ annotations: ['1'] }],
selectedContentSearchAnnotation: ['1'],
});
expect(wrapper.find('Connect(WithPlugins(MiradorMenuButton))[disabled=true]').length).toEqual(2);
});
});
});
import React from 'react';
import { shallow } from 'enzyme';
import { SearchPanelNavigation } from '../../../src/components/SearchPanelNavigation';
/**
* Helper function to create a shallow wrapper around SearchPanelNavigation
*/
function createWrapper(props) {
return shallow(
<SearchPanelNavigation
companionWindowId="cw"
windowId="window"
{...props}
/>,
);
}
describe('SearchPanelNavigation', () => {
describe('when searchHits are available', () => {
it('renders text with buttons', () => {
const selectContentSearchAnnotation = jest.fn();
const wrapper = createWrapper({
searchHits: [{ annotations: ['1'] }, { annotations: ['2'] }, { annotations: ['3'] }],
selectContentSearchAnnotation,
selectedContentSearchAnnotation: ['2'],
});
expect(wrapper.find('WithStyles(ForwardRef(Typography))').text()).toEqual('searchPageSeparator');
expect(wrapper.find('Connect(WithPlugins(MiradorMenuButton))[disabled=false]').length).toEqual(2);
wrapper.find('Connect(WithPlugins(MiradorMenuButton))[disabled=false]').first().props().onClick();
expect(selectContentSearchAnnotation).toHaveBeenCalledWith('window', ['1']);
wrapper.find('Connect(WithPlugins(MiradorMenuButton))[disabled=false]').last().props().onClick();
expect(selectContentSearchAnnotation).toHaveBeenCalledWith('window', ['3']);
});
it('buttons disabled when no next/prev', () => {
const wrapper = createWrapper({
searchHits: [{ annotations: ['1'] }],
selectedContentSearchAnnotation: ['1'],
});
expect(wrapper.find('Connect(WithPlugins(MiradorMenuButton))[disabled=true]').length).toEqual(2);
});
});
});
......@@ -6,13 +6,11 @@ import Downshift from 'downshift';
import TextField from '@material-ui/core/TextField';
import FormControl from '@material-ui/core/FormControl';
import SearchIcon from '@material-ui/icons/SearchSharp';
import ChevronLeftIcon from '@material-ui/icons/ChevronLeftSharp';
import ChevronRightIcon from '@material-ui/icons/ChevronRightSharp';
import InputAdornment from '@material-ui/core/InputAdornment';
import Typography from '@material-ui/core/Typography';
import Paper from '@material-ui/core/Paper';
import MenuItem from '@material-ui/core/MenuItem';
import MiradorMenuButton from '../containers/MiradorMenuButton';
import SearchPanelNavigation from '../containers/SearchPanelNavigation';
/** */
function renderInput(inputProps) {
......@@ -110,34 +108,6 @@ export class SearchPanelControls extends Component {
: suggestions;
}
/** */
nextSearchResult(currentHitIndex) {
const { searchHits, selectContentSearchAnnotation, windowId } = this.props;
selectContentSearchAnnotation(windowId, searchHits[currentHitIndex + 1].annotations);
}
/** */
previousSearchResult(currentHitIndex) {
const { searchHits, selectContentSearchAnnotation, windowId } = this.props;
selectContentSearchAnnotation(windowId, searchHits[currentHitIndex - 1].annotations);
}
/** */
hasNextResult(currentHitIndex) {
const { searchHits } = this.props;
if (searchHits.length === 0) return false;
if (currentHitIndex < searchHits.length - 1) return true;
return false;
}
/** */
hasPreviousResult(currentHitIndex) {
const { searchHits } = this.props;
if (searchHits.length === 0) return false;
if (currentHitIndex > 0) return true;
return false;
}
/** */
handleChange(value) {
this.setState({
......@@ -189,12 +159,9 @@ export class SearchPanelControls extends Component {
/** */
render() {
const {
companionWindowId, t, searchHits, selectedContentSearchAnnotation, classes,
selectOpen,
companionWindowId, t, selectOpen, windowId,
} = this.props;
const currentHitIndex = searchHits
.findIndex(val => val.annotations.includes(selectedContentSearchAnnotation[0]));
const { search } = this.state;
const id = `search-${companionWindowId}`;
return (
......@@ -263,25 +230,7 @@ export class SearchPanelControls extends Component {
</Downshift>
</FormControl>
</form>
{(searchHits.length > 0) && (
<Typography variant="body2" align="center" classes={classes}>
<MiradorMenuButton
aria-label={t('searchPreviousResult')}
disabled={!this.hasPreviousResult(currentHitIndex)}
onClick={() => this.previousSearchResult(currentHitIndex)}
>
<ChevronLeftIcon />
</MiradorMenuButton>
{t('searchPageSeparator', { current: currentHitIndex + 1, total: searchHits.length })}
<MiradorMenuButton
aria-label={t('searchNextResult')}
disabled={!this.hasNextResult(currentHitIndex)}
onClick={() => this.nextSearchResult(currentHitIndex)}
>
<ChevronRightIcon />
</MiradorMenuButton>
</Typography>
)}
<SearchPanelNavigation windowId={windowId} companionWindowId={companionWindowId} />
</>
);
}
......@@ -291,16 +240,12 @@ SearchPanelControls.propTypes = {
autocompleteService: PropTypes.shape({
id: PropTypes.string,
}),
classes: PropTypes.objectOf(PropTypes.string),
companionWindowId: PropTypes.string.isRequired,
fetchSearch: PropTypes.func.isRequired,
query: PropTypes.string,
searchHits: PropTypes.arrayOf(PropTypes.object),
searchService: PropTypes.shape({
id: PropTypes.string,
}).isRequired,
selectContentSearchAnnotation: PropTypes.func.isRequired,
selectedContentSearchAnnotation: PropTypes.arrayOf(PropTypes.string).isRequired,
selectOpen: PropTypes.bool,
t: PropTypes.func,
windowId: PropTypes.string.isRequired,
......@@ -308,9 +253,7 @@ SearchPanelControls.propTypes = {
SearchPanelControls.defaultProps = {
autocompleteService: undefined,
classes: {},
query: '',
searchHits: [],
selectOpen: undefined,
t: key => key,
};
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import ChevronLeftIcon from '@material-ui/icons/ChevronLeftSharp';
import ChevronRightIcon from '@material-ui/icons/ChevronRightSharp';
import Typography from '@material-ui/core/Typography';
import MiradorMenuButton from '../containers/MiradorMenuButton';
/**
* SearchPanelNavigation ~
*/
export class SearchPanelNavigation extends Component {
/** */
nextSearchResult(currentHitIndex) {
const { searchHits, selectContentSearchAnnotation, windowId } = this.props;
selectContentSearchAnnotation(windowId, searchHits[currentHitIndex + 1].annotations);
}
/** */
previousSearchResult(currentHitIndex) {
const { searchHits, selectContentSearchAnnotation, windowId } = this.props;
selectContentSearchAnnotation(windowId, searchHits[currentHitIndex - 1].annotations);
}
/** */
hasNextResult(currentHitIndex) {
const { searchHits } = this.props;
if (searchHits.length === 0) return false;
if (currentHitIndex < searchHits.length - 1) return true;
return false;
}
/** */
hasPreviousResult(currentHitIndex) {
const { searchHits } = this.props;
if (searchHits.length === 0) return false;
if (currentHitIndex > 0) return true;
return false;
}
/**
* Returns the rendered component
*/
render() {
const {
searchHits, selectedContentSearchAnnotation, classes, t,
} = this.props;
const currentHitIndex = searchHits
.findIndex(val => val.annotations.includes(selectedContentSearchAnnotation[0]));
return (
<>
{(searchHits.length > 0) && (
<Typography variant="body2" align="center" classes={classes}>
<MiradorMenuButton
aria-label={t('searchPreviousResult')}
disabled={!this.hasPreviousResult(currentHitIndex)}
onClick={() => this.previousSearchResult(currentHitIndex)}
>
<ChevronLeftIcon />
</MiradorMenuButton>
{t('searchPageSeparator', { current: currentHitIndex + 1, total: searchHits.length })}
<MiradorMenuButton
aria-label={t('searchNextResult')}
disabled={!this.hasNextResult(currentHitIndex)}
onClick={() => this.nextSearchResult(currentHitIndex)}
>
<ChevronRightIcon />
</MiradorMenuButton>
</Typography>
)}
</>
);
}
}
SearchPanelNavigation.propTypes = {
classes: PropTypes.objectOf(PropTypes.string),
searchHits: PropTypes.arrayOf(PropTypes.object),
searchService: PropTypes.shape({
id: PropTypes.string,
}).isRequired,
selectContentSearchAnnotation: PropTypes.func.isRequired,
selectedContentSearchAnnotation: PropTypes.arrayOf(PropTypes.string).isRequired,
t: PropTypes.func,
windowId: PropTypes.string.isRequired,
};
SearchPanelNavigation.defaultProps = {
classes: {},
searchHits: [],
t: key => key,
};
......@@ -8,9 +8,7 @@ import * as actions from '../state/actions';
import {
getManifestAutocompleteService,
getManifestSearchService,
getSearchHitsForCompanionWindow,
getSearchResultsForCompanionWindow,
getSelectedContentSearchAnnotationIds,
} from '../state/selectors';
/**
......@@ -23,9 +21,7 @@ const mapStateToProps = (state, { companionWindowId, windowId }) => {
return {
autocompleteService: getManifestAutocompleteService(state, { windowId }),
query: results && results.query,
searchHits: getSearchHitsForCompanionWindow(state, { companionWindowId, windowId }),
searchService: getManifestSearchService(state, { windowId }),
selectedContentSearchAnnotation: getSelectedContentSearchAnnotationIds(state, { windowId }),
};
};
......@@ -36,20 +32,11 @@ const mapStateToProps = (state, { companionWindowId, windowId }) => {
*/
const mapDispatchToProps = {
fetchSearch: actions.fetchSearch,
selectContentSearchAnnotation: actions.selectContentSearchAnnotation,
};
/** */
const styles = theme => ({
body2: {
marginLeft: '-16px',
width: '100%',
},
});
const enhance = compose(
connect(mapStateToProps, mapDispatchToProps),
withStyles(styles),
withStyles({}),
withTranslation(),
withPlugins('SearchPanelControls'),
);
......
import { compose } from 'redux';
import { connect } from 'react-redux';
import { withTranslation } from 'react-i18next';
import { withStyles } from '@material-ui/core/styles';
import { withPlugins } from '../extend/withPlugins';
import { SearchPanelNavigation } from '../components/SearchPanelNavigation';
import * as actions from '../state/actions';
import {
getSearchHitsForCompanionWindow,
getSelectedContentSearchAnnotationIds,
} from '../state/selectors';
/**
* mapStateToProps - used to hook up connect to state
* @memberof SearchPanelControls
* @private
*/
const mapStateToProps = (state, { companionWindowId, windowId }) => ({
searchHits: getSearchHitsForCompanionWindow(state, { companionWindowId, windowId }),
selectedContentSearchAnnotation: getSelectedContentSearchAnnotationIds(state, { windowId }),
});
/**
* mapDispatchToProps - to hook up connect
* @memberof SearchPanelControls
* @private
*/
const mapDispatchToProps = {
selectContentSearchAnnotation: actions.selectContentSearchAnnotation,
};
/** */
const styles = theme => ({
body2: {
marginLeft: '-16px',
width: '100%',
},
});
const enhance = compose(
connect(mapStateToProps, mapDispatchToProps),
withStyles(styles),
withTranslation(),
withPlugins('SearchPanelNavigation'),
);
export default enhance(SearchPanelNavigation);
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment