Unverified Commit 7c06fdf7 authored by Ryan Lieu's avatar Ryan Lieu Committed by GitHub

Home page component (#190)

* new home page component

* separated home page

* searchbar fix

* separated search bar into separate component

* fixed breadcrumbs to go back to proper url

* cleaned up consolelogs, cleaned up home page component

* addressed comments

* addressed more comments'

* more code styling fixes

* removed unecessary files, fixed styling for home page to include larger margins

* making breadcrumb an isolated component

* gave breadcrumbs default props, fixed styling, removed unecessary API call on componentDidMount

* added tests

* fixed homepage tests, cleaned up code

* removed extraneous import for homepage test

* fixed breadcrumb/test issues?

* cleaned up breadcrumb code

* fixed breadcrumb logic

* removed default props

* one last breadcrumb fix
parent e4c02787
import * as React from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { RouteComponentProps } from 'react-router';
// TODO: Use css-modules instead of 'import'
import './styles.scss';
import BookmarkList from 'components/common/Bookmark/BookmarkList';
import PopularTables from 'components/common/PopularTables';
import { SearchAllReset } from 'ducks/search/types';
import { searchReset } from 'ducks/search/reducer';
import SearchBar from 'components/SearchPage/SearchBar';
export interface DispatchFromProps {
searchReset: () => SearchAllReset;
}
export type HomePageProps = DispatchFromProps & RouteComponentProps<any>;
export class HomePage extends React.Component<HomePageProps> {
constructor(props) {
super(props);
}
componentDidMount() {
this.props.searchReset();
}
render() {
return (
<div className="container home-page">
<div className="row">
<div className="col-xs-12 col-md-offset-1 col-md-10">
<SearchBar />
<div className="home-element-container">
<BookmarkList />
</div>
<div className="home-element-container">
<PopularTables />
</div>
</div>
</div>
</div>
);
}
}
export const mapDispatchToProps = (dispatch: any) => {
return bindActionCreators({ searchReset } , dispatch);
};
export default connect<DispatchFromProps>(null, mapDispatchToProps)(HomePage);
@import 'variables';
.home-page {
.home-element-container {
margin-top: 64px;
}
@media (max-width: $screen-sm-max) {
.home-element-container {
margin-top: 32px;
}
}
}
import * as React from 'react';
import { shallow } from 'enzyme';
import { mapDispatchToProps, HomePage, HomePageProps } from '../';
import SearchBar from 'components/SearchPage/SearchBar';
import BookmarkList from 'components/common/Bookmark/BookmarkList';
import PopularTables from 'components/common/PopularTables';
describe('HomePage', () => {
const setup = (propOverrides?: Partial<HomePageProps>) => {
const props: HomePageProps = {
searchReset: jest.fn(),
history: {
length: 2,
action: "POP",
location: jest.fn() as any,
push: jest.fn(),
replace: jest.fn(),
go: jest.fn(),
goBack: jest.fn(),
goForward: jest.fn(),
block: jest.fn(),
createHref: jest.fn(),
listen: jest.fn(),
},
location: {
search: '/search?searchTerm=testName&selectedTab=table&pageIndex=1',
pathname: 'mockstr',
state: jest.fn(),
hash: 'mockstr',
},
match: jest.fn() as any,
staticContext: jest.fn() as any,
...propOverrides
};
const wrapper = shallow<HomePage>(<HomePage {...props} />)
return { props, wrapper };
};
let props;
let wrapper;
beforeAll(() => {
const setupResult = setup();
props = setupResult.props;
wrapper = setupResult.wrapper;
});
describe('render', () => {
it('contains Searchbar, BookmarkList, and PopularTables', () => {
expect(wrapper.contains(<SearchBar />));
expect(wrapper.contains(<BookmarkList />));
expect(wrapper.contains(<PopularTables />));
});
});
describe('componentDidMount', () => {
it('calls searchReset', () => {
const searchResetSpy = jest.spyOn(props, 'searchReset');
wrapper.instance().componentDidMount();
expect(searchResetSpy).toHaveBeenCalled;
});
});
});
describe('mapDispatchToProps', () => {
let dispatch;
let result;
beforeAll(() => {
dispatch = jest.fn(() => Promise.resolve());
result = mapDispatchToProps(dispatch);
});
it('sets searchReset on the props', () => {
expect(result.searchReset).toBeInstanceOf(Function);
});
});
...@@ -10,7 +10,7 @@ const NotFoundPage: React.SFC<any> = () => { ...@@ -10,7 +10,7 @@ const NotFoundPage: React.SFC<any> = () => {
return ( return (
<DocumentTitle title="404 Page Not Found - Amundsen"> <DocumentTitle title="404 Page Not Found - Amundsen">
<div className="container not-found-page"> <div className="container not-found-page">
<Breadcrumb path='/' text='Home'/> <Breadcrumb path="/" text="Home" />
<h1>404 Page Not Found</h1> <h1>404 Page Not Found</h1>
<img className="icon icon-alert"/> <img className="icon icon-alert"/>
</div> </div>
......
...@@ -86,7 +86,8 @@ export class ProfilePage extends React.Component<ProfilePageProps> { ...@@ -86,7 +86,8 @@ export class ProfilePage extends React.Component<ProfilePageProps> {
<div className="container profile-page"> <div className="container profile-page">
<div className="row"> <div className="row">
<div className="col-xs-12 col-md-offset-1 col-md-10"> <div className="col-xs-12 col-md-offset-1 col-md-10">
<Breadcrumb path='/' text='Search Results'/> {/* remove hardcode to home when this page is ready for production */}
<Breadcrumb path="/" text="Home" />
<div className="profile-header"> <div className="profile-header">
<div id="profile-avatar" className="profile-avatar"> <div id="profile-avatar" className="profile-avatar">
{ {
......
...@@ -95,7 +95,7 @@ describe('ProfilePage', () => { ...@@ -95,7 +95,7 @@ describe('ProfilePage', () => {
it('renders Breadcrumb with correct props', () => { it('renders Breadcrumb with correct props', () => {
expect(wrapper.find(Breadcrumb).props()).toMatchObject({ expect(wrapper.find(Breadcrumb).props()).toMatchObject({
path: '/', path: '/',
text: 'Search Results', text: 'Home',
}); });
}); });
......
import * as React from 'react'; import * as React from 'react';
import { connect } from 'react-redux';
import { RouteComponentProps, withRouter } from 'react-router';
// TODO: Use css-modules instead of 'import' // TODO: Use css-modules instead of 'import'
import './styles.scss'; import './styles.scss';
...@@ -11,24 +13,28 @@ import { ...@@ -11,24 +13,28 @@ import {
SYNTAX_ERROR_PREFIX, SYNTAX_ERROR_PREFIX,
SYNTAX_ERROR_SPACING_SUFFIX, SYNTAX_ERROR_SPACING_SUFFIX,
} from './constants'; } from './constants';
import { GlobalState } from 'ducks/rootReducer';
export interface SearchBarProps { export interface StateFromProps {
handleValueSubmit: (term: string) => void; searchTerm: string;
}
export interface OwnProps {
placeholder?: string; placeholder?: string;
searchTerm?: string;
subText?: string; subText?: string;
} }
export type SearchBarProps = StateFromProps & OwnProps & RouteComponentProps<any>;
interface SearchBarState { interface SearchBarState {
subTextClassName: string; subTextClassName: string;
searchTerm: string; searchTerm: string;
subText: string; subText: string;
} }
class SearchBar extends React.Component<SearchBarProps, SearchBarState> { export class SearchBar extends React.Component<SearchBarProps, SearchBarState> {
public static defaultProps: Partial<SearchBarProps> = { public static defaultProps: Partial<SearchBarProps> = {
placeholder: PLACEHOLDER_DEFAULT, placeholder: PLACEHOLDER_DEFAULT,
searchTerm: '',
subText: SUBTEXT_DEFAULT, subText: SUBTEXT_DEFAULT,
}; };
...@@ -54,7 +60,8 @@ class SearchBar extends React.Component<SearchBarProps, SearchBarState> { ...@@ -54,7 +60,8 @@ class SearchBar extends React.Component<SearchBarProps, SearchBarState> {
handleValueSubmit = (event: React.FormEvent<HTMLFormElement>) : void => { handleValueSubmit = (event: React.FormEvent<HTMLFormElement>) : void => {
event.preventDefault(); event.preventDefault();
if (this.isFormValid()) { if (this.isFormValid()) {
this.props.handleValueSubmit(this.state.searchTerm); const pathName = `/search?searchTerm=${this.state.searchTerm}&selectedTab=table&pageIndex=0`;
this.props.history.push(pathName);
} }
}; };
...@@ -111,4 +118,10 @@ class SearchBar extends React.Component<SearchBarProps, SearchBarState> { ...@@ -111,4 +118,10 @@ class SearchBar extends React.Component<SearchBarProps, SearchBarState> {
} }
} }
export default SearchBar; export const mapStateToProps = (state: GlobalState) => {
return {
searchTerm: state.search.search_term,
};
};
export default connect<StateFromProps>(mapStateToProps, null)(withRouter(SearchBar));
...@@ -2,7 +2,7 @@ import * as React from 'react'; ...@@ -2,7 +2,7 @@ import * as React from 'react';
import { shallow } from 'enzyme'; import { shallow } from 'enzyme';
import SearchBar, { SearchBarProps } from '../'; import { mapStateToProps, SearchBar, SearchBarProps } from '../';
import { import {
ERROR_CLASSNAME, ERROR_CLASSNAME,
SUBTEXT_DEFAULT, SUBTEXT_DEFAULT,
...@@ -10,6 +10,7 @@ import { ...@@ -10,6 +10,7 @@ import {
SYNTAX_ERROR_PREFIX, SYNTAX_ERROR_PREFIX,
SYNTAX_ERROR_SPACING_SUFFIX, SYNTAX_ERROR_SPACING_SUFFIX,
} from '../constants'; } from '../constants';
import globalState from 'fixtures/globalState';
describe('SearchBar', () => { describe('SearchBar', () => {
const valueChangeMockEvent = { target: { value: 'Data Resources' } }; const valueChangeMockEvent = { target: { value: 'Data Resources' } };
...@@ -18,7 +19,28 @@ describe('SearchBar', () => { ...@@ -18,7 +19,28 @@ describe('SearchBar', () => {
const setup = (propOverrides?: Partial<SearchBarProps>) => { const setup = (propOverrides?: Partial<SearchBarProps>) => {
const props: SearchBarProps = { const props: SearchBarProps = {
handleValueSubmit: jest.fn(), searchTerm: '',
history: {
length: 2,
action: "POP",
location: jest.fn() as any,
push: jest.fn(),
replace: jest.fn(),
go: jest.fn(),
goBack: jest.fn(),
goForward: jest.fn(),
block: jest.fn(),
createHref: jest.fn(),
listen: jest.fn(),
},
location: {
search: '/search?searchTerm=testName&selectedTab=table&pageIndex=1',
pathname: 'mockstr',
state: jest.fn(),
hash: 'mockstr',
},
match: jest.fn() as any,
staticContext: jest.fn() as any,
...propOverrides ...propOverrides
}; };
const wrapper = shallow<SearchBar>(<SearchBar {...props} />) const wrapper = shallow<SearchBar>(<SearchBar {...props} />)
...@@ -81,14 +103,14 @@ describe('SearchBar', () => { ...@@ -81,14 +103,14 @@ describe('SearchBar', () => {
it('submits with correct props if isFormValid()', () => { it('submits with correct props if isFormValid()', () => {
// @ts-ignore: mocked events throw type errors // @ts-ignore: mocked events throw type errors
wrapper.instance().handleValueSubmit(submitMockEvent); wrapper.instance().handleValueSubmit(submitMockEvent);
expect(props.handleValueSubmit).toHaveBeenCalledWith(wrapper.state().searchTerm); expect(props.history.push).toHaveBeenCalledWith(`/search?searchTerm=${wrapper.state().searchTerm}&selectedTab=table&pageIndex=0`);
}); });
it('does not submit if !isFormValid()', () => { it('does not submit if !isFormValid()', () => {
const { props, wrapper } = setup({ searchTerm: 'tag:tag1 tag:tag2' }); const { props, wrapper } = setup({ searchTerm: 'tag:tag1 tag:tag2' });
// @ts-ignore: mocked events throw type errors // @ts-ignore: mocked events throw type errors
wrapper.instance().handleValueSubmit(submitMockEvent); wrapper.instance().handleValueSubmit(submitMockEvent);
expect(props.handleValueSubmit).not.toHaveBeenCalled(); expect(props.history.push).not.toHaveBeenCalled();
}); });
}); });
...@@ -222,3 +244,14 @@ describe('SearchBar', () => { ...@@ -222,3 +244,14 @@ describe('SearchBar', () => {
}); });
}); });
}); });
describe('mapStateToProps', () => {
let result;
beforeAll(() => {
result = mapStateToProps(globalState);
});
it('sets searchTerm on the props', () => {
expect(result.searchTerm).toEqual(globalState.search.search_term);
});
});
...@@ -9,9 +9,6 @@ import { RouteComponentProps } from 'react-router'; ...@@ -9,9 +9,6 @@ import { RouteComponentProps } from 'react-router';
import SearchBar from './SearchBar'; import SearchBar from './SearchBar';
import SearchList from './SearchList'; import SearchList from './SearchList';
import LoadingSpinner from 'components/common/LoadingSpinner'; import LoadingSpinner from 'components/common/LoadingSpinner';
import PopularTables from 'components/common/PopularTables';
import BookmarkList from 'components/common/Bookmark/BookmarkList'
import InfoButton from 'components/common/InfoButton'; import InfoButton from 'components/common/InfoButton';
import { ResourceType, TableResource } from 'interfaces'; import { ResourceType, TableResource } from 'interfaces';
import TabsComponent from 'components/common/Tabs'; import TabsComponent from 'components/common/Tabs';
...@@ -78,7 +75,7 @@ export class SearchPage extends React.Component<SearchPageProps, SearchPageState ...@@ -78,7 +75,7 @@ export class SearchPage extends React.Component<SearchPageProps, SearchPageState
const { searchTerm, pageIndex, selectedTab } = params; const { searchTerm, pageIndex, selectedTab } = params;
const { term, index, currentTab } = this.getSanitizedUrlParams(searchTerm, pageIndex, selectedTab); const { term, index, currentTab } = this.getSanitizedUrlParams(searchTerm, pageIndex, selectedTab);
this.setState({ selectedTab: currentTab }); this.setState({ selectedTab: currentTab });
if (term !== "") { if (term !== '') {
this.props.searchAll(term, this.createSearchOptions(index, currentTab)); this.props.searchAll(term, this.createSearchOptions(index, currentTab));
if (currentTab !== selectedTab || pageIndex !== index) { if (currentTab !== selectedTab || pageIndex !== index) {
this.updatePageUrl(term, currentTab, index); this.updatePageUrl(term, currentTab, index);
...@@ -137,10 +134,6 @@ export class SearchPage extends React.Component<SearchPageProps, SearchPageState ...@@ -137,10 +134,6 @@ export class SearchPage extends React.Component<SearchPageProps, SearchPageState
return 0; return 0;
}; };
onSearchBarSubmit = (searchTerm: string): void => {
this.updatePageUrl(searchTerm, this.state.selectedTab,0);
};
onPaginationChange = (pageNumber: number): void => { onPaginationChange = (pageNumber: number): void => {
const index = pageNumber - 1; const index = pageNumber - 1;
this.props.searchResource(this.state.selectedTab, this.props.searchTerm, index); this.props.searchResource(this.state.selectedTab, this.props.searchTerm, index);
...@@ -158,15 +151,6 @@ export class SearchPage extends React.Component<SearchPageProps, SearchPageState ...@@ -158,15 +151,6 @@ export class SearchPage extends React.Component<SearchPageProps, SearchPageState
this.props.history.push(pathName); this.props.history.push(pathName);
}; };
renderPopularTables = () => {
return (
<div className="search-list-container">
<BookmarkList />
<PopularTables />
</div>
)
};
renderSearchResults = () => { renderSearchResults = () => {
const tabConfig = [ const tabConfig = [
{ {
...@@ -246,10 +230,7 @@ export class SearchPage extends React.Component<SearchPageProps, SearchPageState ...@@ -246,10 +230,7 @@ export class SearchPage extends React.Component<SearchPageProps, SearchPageState
if (this.props.isLoading) { if (this.props.isLoading) {
return (<LoadingSpinner/>); return (<LoadingSpinner/>);
} }
if (this.props.searchTerm.length > 0) { return this.renderSearchResults();
return this.renderSearchResults();
}
return this.renderPopularTables();
}; };
render() { render() {
...@@ -258,7 +239,7 @@ export class SearchPage extends React.Component<SearchPageProps, SearchPageState ...@@ -258,7 +239,7 @@ export class SearchPage extends React.Component<SearchPageProps, SearchPageState
<div className="container search-page"> <div className="container search-page">
<div className="row"> <div className="row">
<div className="col-xs-12 col-md-offset-1 col-md-10"> <div className="col-xs-12 col-md-offset-1 col-md-10">
<SearchBar handleValueSubmit={ this.onSearchBarSubmit } searchTerm={ searchTerm }/> <SearchBar />
{ this.renderContent() } { this.renderContent() }
</div> </div>
</div> </div>
......
...@@ -28,7 +28,6 @@ import SearchList from '../SearchList'; ...@@ -28,7 +28,6 @@ import SearchList from '../SearchList';
import globalState from 'fixtures/globalState'; import globalState from 'fixtures/globalState';
import LoadingSpinner from 'components/common/LoadingSpinner'; import LoadingSpinner from 'components/common/LoadingSpinner';
import BookmarkList from 'components/common/Bookmark/BookmarkList'; import BookmarkList from 'components/common/Bookmark/BookmarkList';
import PopularTables from 'components/common/PopularTables';
describe('SearchPage', () => { describe('SearchPage', () => {
const setStateSpy = jest.spyOn(SearchPage.prototype, 'setState'); const setStateSpy = jest.spyOn(SearchPage.prototype, 'setState');
...@@ -431,26 +430,6 @@ describe('SearchPage', () => { ...@@ -431,26 +430,6 @@ describe('SearchPage', () => {
}); });
}); });
describe('onSearchBarSubmit', () => {
let props;
let wrapper;
let updatePageUrlSpy;
beforeAll(() => {
const setupResult = setup();
props = setupResult.props;
wrapper = setupResult.wrapper;
updatePageUrlSpy = jest.spyOn(wrapper.instance(), 'updatePageUrl');
wrapper.instance().onSearchBarSubmit('searchTerm');
});
it('call updatePageUrl with correct parameters', () => {
expect(updatePageUrlSpy).toHaveBeenCalledWith('searchTerm', wrapper.state().selectedTab, 0);
});
});
describe('onPaginationChange', () => { describe('onPaginationChange', () => {
const testIndex = 10; const testIndex = 10;
let props; let props;
...@@ -618,11 +597,6 @@ describe('SearchPage', () => { ...@@ -618,11 +597,6 @@ describe('SearchPage', () => {
}); });
describe('renderContent', () => { describe('renderContent', () => {
it('renders popular tables if searchTerm is empty', () => {
const {props, wrapper} = setup({ searchTerm: '' });
expect(wrapper.instance().renderContent()).toEqual(wrapper.instance().renderPopularTables());
});
it('renders search results when given search term', () => { it('renders search results when given search term', () => {
const {props, wrapper} = setup({ searchTerm: 'test' }); const {props, wrapper} = setup({ searchTerm: 'test' });
expect(wrapper.instance().renderContent()).toEqual(wrapper.instance().renderSearchResults()); expect(wrapper.instance().renderContent()).toEqual(wrapper.instance().renderSearchResults());
...@@ -634,15 +608,6 @@ describe('SearchPage', () => { ...@@ -634,15 +608,6 @@ describe('SearchPage', () => {
}); });
}); });
describe('renderPopularTables', () => {
it('renders bookmark list and popular tables', () => {
const {props, wrapper} = setup();
wrapper.instance().renderPopularTables();
expect(wrapper.contains(<BookmarkList />));
expect(wrapper.contains(<PopularTables />));
});
});
describe('renderSearchResults', () => { describe('renderSearchResults', () => {
it('renders TabsComponent with correct props', () => { it('renders TabsComponent with correct props', () => {
const { props, wrapper } = setup({ searchTerm: 'test search' }); const { props, wrapper } = setup({ searchTerm: 'test search' });
...@@ -680,10 +645,7 @@ describe('SearchPage', () => { ...@@ -680,10 +645,7 @@ describe('SearchPage', () => {
it('renders SearchBar with correct props', () => { it('renders SearchBar with correct props', () => {
const { props, wrapper } = setup(); const { props, wrapper } = setup();
expect(wrapper.find(SearchBar).props()).toMatchObject({ expect(wrapper.find(SearchBar).exists()).toBeTruthy();
handleValueSubmit: wrapper.instance().onSearchBarSubmit,
searchTerm: props.searchTerm,
});
}); });
it('calls renderSearchResults if searchTerm is not empty string', () => { it('calls renderSearchResults if searchTerm is not empty string', () => {
...@@ -692,13 +654,6 @@ describe('SearchPage', () => { ...@@ -692,13 +654,6 @@ describe('SearchPage', () => {
wrapper.setProps(props); wrapper.setProps(props);
expect(renderSearchResultsSpy).toHaveBeenCalled(); expect(renderSearchResultsSpy).toHaveBeenCalled();
}); });
it('calls renderPopularTables is searchTerm is empty string', () => {
const { props, wrapper } = setup({ searchTerm: '' });
const renderPopularTablesSpy = jest.spyOn(wrapper.instance(), 'renderPopularTables');
wrapper.setProps(props);
expect(renderPopularTablesSpy).toHaveBeenCalled();
});
}); });
}); });
......
...@@ -118,7 +118,6 @@ export class TableDetail extends React.Component<TableDetailProps & RouteCompone ...@@ -118,7 +118,6 @@ export class TableDetail extends React.Component<TableDetailProps & RouteCompone
const params = qs.parse(this.props.location.search); const params = qs.parse(this.props.location.search);
const searchIndex = params['index']; const searchIndex = params['index'];
const source = params['source']; const source = params['source'];
/* update the url stored in the browser history to remove params used for logging purposes */ /* update the url stored in the browser history to remove params used for logging purposes */
if (searchIndex !== undefined) { if (searchIndex !== undefined) {
window.history.replaceState({}, '', `${window.location.origin}${window.location.pathname}`); window.history.replaceState({}, '', `${window.location.origin}${window.location.pathname}`);
...@@ -331,14 +330,14 @@ export class TableDetail extends React.Component<TableDetailProps & RouteCompone ...@@ -331,14 +330,14 @@ export class TableDetail extends React.Component<TableDetailProps & RouteCompone
} else if (this.state.statusCode === 500) { } else if (this.state.statusCode === 500) {
innerContent = ( innerContent = (
<div className="container error-label"> <div className="container error-label">
<Breadcrumb path='/' text='Search Results'/> <Breadcrumb />
<label className="d-block m-auto">Something went wrong...</label> <label className="d-block m-auto">Something went wrong...</label>
</div> </div>
) )
} else { } else {
innerContent = ( innerContent = (
<div className="container table-detail"> <div className="container table-detail">
<Breadcrumb path='/' text='Search Results'/> <Breadcrumb />
<div className="row"> <div className="row">
<div className="detail-header col-xs-12 col-md-7 col-lg-8"> <div className="detail-header col-xs-12 col-md-7 col-lg-8">
<h1 className="detail-header-text"> <h1 className="detail-header-text">
......
import * as React from 'react'; import * as React from 'react';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import './styles.scss'; import './styles.scss';
import { GlobalState } from 'ducks/rootReducer';
export interface BreadcrumbProps { export interface OwnProps {
path: string; path?: string;
text: string; text?: string;
} }
const Breadcrumb: React.SFC<BreadcrumbProps> = ({ path, text }) => { export interface StateFromProps {
searchTerm: string;
}
export type BreadcrumbProps = OwnProps & StateFromProps;
export const Breadcrumb: React.SFC<BreadcrumbProps> = (props) => {
let path = props.path;
let text = props.text;
if (!path && !text) {
path = '/';
text = 'Home';
if (props.searchTerm) {
path = `/search?searchTerm=${props.searchTerm}&selectedTab=table&pageIndex=0`
text = 'Search Results'
}
}
return ( return (
<div className="amundsen-breadcrumb"> <div className="amundsen-breadcrumb">
<Link to={path}> <Link to={path}>
...@@ -21,9 +39,10 @@ const Breadcrumb: React.SFC<BreadcrumbProps> = ({ path, text }) => { ...@@ -21,9 +39,10 @@ const Breadcrumb: React.SFC<BreadcrumbProps> = ({ path, text }) => {
); );
}; };
Breadcrumb.defaultProps = { export const mapStateToProps = (state: GlobalState) => {
path: '/', return {
text: 'Home', searchTerm: state.search.search_term,
};
}; };
export default Breadcrumb; export default connect<StateFromProps>(mapStateToProps, null)(Breadcrumb);
...@@ -3,29 +3,70 @@ import * as React from 'react'; ...@@ -3,29 +3,70 @@ import * as React from 'react';
import { shallow } from 'enzyme'; import { shallow } from 'enzyme';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import Breadcrumb, { BreadcrumbProps } from '../'; import { Breadcrumb, BreadcrumbProps } from '../';
describe('Breadcrumb', () => { describe('Breadcrumb', () => {
let props: BreadcrumbProps; let props: BreadcrumbProps;
let subject; let subject;
describe('render', () => {
beforeEach(() => { beforeEach(() => {
props = { props = {
path: 'testPath', path: 'testPath',
text: 'testText', text: 'testText',
}; searchTerm: '',
subject = shallow(<Breadcrumb {...props} />); };
subject = shallow(<Breadcrumb {...props} />);
}); });
describe('render', () => { it('renders Link with correct path', () => {
it('renders Link with correct path', () => { expect(subject.find(Link).props()).toMatchObject({
expect(subject.find(Link).props()).toMatchObject({ to: props.path,
to: props.path, });
}); });
});
it('renders button with correct text within the Link', () => {
expect(subject.find(Link).find('button').text()).toEqual(props.text);
});
});
describe('render with existing searchTerm', () => {
beforeEach(() => {
props = {
searchTerm: 'testTerm',
};
subject = shallow(<Breadcrumb {...props} />);
});
it('renders Link with correct path', () => {
expect(subject.find(Link).props()).toMatchObject({
to: '/search?searchTerm=testTerm&selectedTab=table&pageIndex=0',
});
});
it('renders button with correct text within the Link', () => {
expect(subject.find(Link).find('button').text()).toEqual('Search Results');
});
});
describe('render with existing searchTerm and prop overrides', () => {
beforeEach(() => {
props = {
path: 'testPath',
text: 'testText',
searchTerm: 'testTerm',
};
subject = shallow(<Breadcrumb {...props} />);
});
it('renders Link with correct path', () => {
expect(subject.find(Link).props()).toMatchObject({
to: 'testPath',
});
});
it('renders button with correct text within the Link', () => { it('renders button with correct text within the Link', () => {
expect(subject.find(Link).find('button').text()).toEqual(props.text); expect(subject.find(Link).find('button').text()).toEqual('testText');
});
}); });
});
}); });
...@@ -2,6 +2,7 @@ import { ...@@ -2,6 +2,7 @@ import {
SearchAll, SearchAll,
SearchAllOptions, SearchAllOptions,
SearchAllRequest, SearchAllRequest,
SearchAllReset,
SearchAllResponse, SearchAllResponse,
SearchResource, SearchResource,
SearchResourceRequest, SearchResourceRequest,
...@@ -12,7 +13,7 @@ import { ...@@ -12,7 +13,7 @@ import {
} from './types'; } from './types';
import { ResourceType } from 'interfaces'; import { ResourceType } from 'interfaces';
export type SearchReducerAction = SearchAllResponse | SearchResourceResponse | SearchAllRequest | SearchResourceRequest; export type SearchReducerAction = SearchAllResponse | SearchResourceResponse | SearchAllRequest | SearchResourceRequest | SearchAllReset;
export interface SearchReducerState { export interface SearchReducerState {
search_term: string; search_term: string;
...@@ -39,6 +40,12 @@ export function searchResource(resource: ResourceType, term: string, pageIndex: ...@@ -39,6 +40,12 @@ export function searchResource(resource: ResourceType, term: string, pageIndex:
}; };
} }
export function searchReset(): SearchAllReset {
return {
type: SearchAll.RESET,
};
}
const initialState: SearchReducerState = { const initialState: SearchReducerState = {
search_term: '', search_term: '',
isLoading: false, isLoading: false,
...@@ -62,6 +69,8 @@ const initialState: SearchReducerState = { ...@@ -62,6 +69,8 @@ const initialState: SearchReducerState = {
export default function reducer(state: SearchReducerState = initialState, action: SearchReducerAction): SearchReducerState { export default function reducer(state: SearchReducerState = initialState, action: SearchReducerAction): SearchReducerState {
switch (action.type) { switch (action.type) {
// Updates search term to reflect action // Updates search term to reflect action
case SearchAll.RESET:
return initialState;
case SearchAll.ACTION: case SearchAll.ACTION:
return { return {
...state, ...state,
......
...@@ -30,6 +30,7 @@ export enum SearchAll { ...@@ -30,6 +30,7 @@ export enum SearchAll {
ACTION = 'amundsen/search/SEARCH_ALL', ACTION = 'amundsen/search/SEARCH_ALL',
SUCCESS = 'amundsen/search/SEARCH_ALL_SUCCESS', SUCCESS = 'amundsen/search/SEARCH_ALL_SUCCESS',
FAILURE = 'amundsen/search/SEARCH_ALL_FAILURE', FAILURE = 'amundsen/search/SEARCH_ALL_FAILURE',
RESET = 'amundsen/search/SEARCH_ALL_RESET',
} }
export interface SearchAllOptions { export interface SearchAllOptions {
...@@ -49,6 +50,9 @@ export interface SearchAllResponse { ...@@ -49,6 +50,9 @@ export interface SearchAllResponse {
payload?: SearchReducerState; payload?: SearchReducerState;
} }
export interface SearchAllReset {
type: SearchAll.RESET;
}
/* searchResource - Search a single resource type */ /* searchResource - Search a single resource type */
export enum SearchResource { export enum SearchResource {
......
...@@ -13,11 +13,12 @@ import AnnouncementPage from './components/AnnouncementPage'; ...@@ -13,11 +13,12 @@ import AnnouncementPage from './components/AnnouncementPage';
import BrowsePage from './components/BrowsePage'; import BrowsePage from './components/BrowsePage';
import Feedback from './components/Feedback'; import Feedback from './components/Feedback';
import Footer from './components/Footer'; import Footer from './components/Footer';
import HomePage from './components/HomePage'
import NavBar from './components/NavBar'; import NavBar from './components/NavBar';
import NotFoundPage from './components/NotFoundPage'; import NotFoundPage from './components/NotFoundPage';
import Preloader from "components/common/Preloader"; import Preloader from "components/common/Preloader";
import ProfilePage from './components/ProfilePage'; import ProfilePage from './components/ProfilePage';
import SearchPage from './components/SearchPage'; import SearchPage from './components/SearchPage';
import TableDetail from './components/TableDetail'; import TableDetail from './components/TableDetail';
import rootReducer from './ducks/rootReducer'; import rootReducer from './ducks/rootReducer';
...@@ -43,7 +44,7 @@ ReactDOM.render( ...@@ -43,7 +44,7 @@ ReactDOM.render(
<Route path="/search" component={SearchPage} /> <Route path="/search" component={SearchPage} />
<Route path="/user/:userId" component={ProfilePage} /> <Route path="/user/:userId" component={ProfilePage} />
<Route path="/404" component={NotFoundPage} /> <Route path="/404" component={NotFoundPage} />
<Route path="/" component={SearchPage} /> <Route path="/" component={HomePage} />
</Switch> </Switch>
<Feedback /> <Feedback />
<Footer /> <Footer />
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment