Unverified Commit 670cbdfc authored by Daniel's avatar Daniel Committed by GitHub

Create Generic Paginated Resource List (#193)

- Created a generic ResourceList with optional Pagination
- Renamed 'BookmarkList' to 'MyBookmarks'
- Converted usages in 'PopularTables', 'SearchPage', and 'MyBookmarks'
parent 7c06fdf7
......@@ -6,7 +6,7 @@ import { RouteComponentProps } from 'react-router';
// TODO: Use css-modules instead of 'import'
import './styles.scss';
import BookmarkList from 'components/common/Bookmark/BookmarkList';
import MyBookmarks from 'components/common/Bookmark/MyBookmarks';
import PopularTables from 'components/common/PopularTables';
import { SearchAllReset } from 'ducks/search/types';
import { searchReset } from 'ducks/search/reducer';
......@@ -35,7 +35,7 @@ export class HomePage extends React.Component<HomePageProps> {
<div className="col-xs-12 col-md-offset-1 col-md-10">
<SearchBar />
<div className="home-element-container">
<BookmarkList />
<MyBookmarks />
</div>
<div className="home-element-container">
<PopularTables />
......
......@@ -5,7 +5,7 @@ import { shallow } from 'enzyme';
import { mapDispatchToProps, HomePage, HomePageProps } from '../';
import SearchBar from 'components/SearchPage/SearchBar';
import BookmarkList from 'components/common/Bookmark/BookmarkList';
import MyBookmarks from 'components/common/Bookmark/MyBookmarks';
import PopularTables from 'components/common/PopularTables';
describe('HomePage', () => {
......@@ -49,7 +49,7 @@ describe('HomePage', () => {
describe('render', () => {
it('contains Searchbar, BookmarkList, and PopularTables', () => {
expect(wrapper.contains(<SearchBar />));
expect(wrapper.contains(<BookmarkList />));
expect(wrapper.contains(<MyBookmarks />));
expect(wrapper.contains(<PopularTables />));
});
});
......
......@@ -7,7 +7,7 @@ import AppConfig from 'config/config';
import { LinkConfig } from 'config/config-types';
import { GlobalState } from 'ducks/rootReducer';
import { LoggedInUser } from 'ducks/user/types';
import { logClick } from "ducks/utilMethods";
import { logClick } from 'ducks/utilMethods';
import './styles.scss';
......
import * as React from 'react';
import ResourceListItem from 'components/common/ResourceListItem';
import { Resource } from 'interfaces';
export interface SearchListProps {
results?: Resource[];
params?: SearchListParams;
}
export interface SearchListParams {
source?: string;
paginationStartIndex?: number;
}
const SearchList: React.SFC<SearchListProps> = ({ results, params }) => {
const { source, paginationStartIndex } = params;
return (
<ul className="list-group">
{
results.map((resource, index) => {
const logging = { source, index: paginationStartIndex + index };
return <ResourceListItem item={ resource } logging={ logging } key={ index } />;
})
}
</ul>
);
};
SearchList.defaultProps = {
results: [],
params: {},
};
export default SearchList;
import * as React from 'react';
import { shallow } from 'enzyme';
import { Resource, ResourceType } from 'interfaces';
import ResourceListItem from 'components/common/ResourceListItem';
import SearchList, { SearchListProps, SearchListParams } from '../';
describe('SearchList', () => {
const setup = (propOverrides?: Partial<SearchListProps>) => {
const props: SearchListProps = {
results: [
{ type: ResourceType.table },
{ type: ResourceType.user },
],
params: {
source: 'testSource',
paginationStartIndex: 0,
},
...propOverrides
};
const wrapper = shallow(<SearchList {...props} />);
return { props, wrapper };
};
describe('render', () => {
let props;
let wrapper;
beforeAll(() => {
const setupResult = setup();
props = setupResult.props;
wrapper = setupResult.wrapper;
});
it('renders unordered list', () => {
expect(wrapper.type()).toEqual('ul');
});
it('renders unordered list w/ correct className', () => {
expect(wrapper.props()).toMatchObject({
className: 'list-group',
});
});
it('renders a ResourceListItem for each result', () => {
const content = wrapper.find(ResourceListItem);
expect(content.length).toEqual(props.results.length);
});
it('passes correct props to each ResourceListItem', () => {
const content = wrapper.find(ResourceListItem);
content.forEach((contentItem, index) => {
expect(contentItem.props()).toMatchObject({
item: props.results[index],
logging: {
source: props.params.source,
index: props.params.paginationStartIndex + index,
}
})
});
});
});
});
......@@ -3,14 +3,14 @@ import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as DocumentTitle from 'react-document-title';
import * as qs from 'simple-query-string';
import Pagination from 'react-js-pagination';
import { RouteComponentProps } from 'react-router';
import SearchBar from './SearchBar';
import SearchList from './SearchList';
import LoadingSpinner from 'components/common/LoadingSpinner';
import { ResourceType } from 'interfaces';
import InfoButton from 'components/common/InfoButton';
import { ResourceType, TableResource } from 'interfaces';
import ResourceList from 'components/common/ResourceList';
import TabsComponent from 'components/common/Tabs';
import { GlobalState } from 'ducks/rootReducer';
......@@ -30,7 +30,6 @@ import './styles.scss';
import {
DOCUMENT_TITLE_SUFFIX,
PAGE_INDEX_ERROR_MESSAGE,
PAGINATION_PAGE_RANGE,
RESULTS_PER_PAGE,
SEARCH_ERROR_MESSAGE_INFIX,
SEARCH_ERROR_MESSAGE_PREFIX,
......@@ -134,8 +133,7 @@ export class SearchPage extends React.Component<SearchPageProps, SearchPageState
return 0;
};
onPaginationChange = (pageNumber: number): void => {
const index = pageNumber - 1;
onPaginationChange = (index: number): void => {
this.props.searchResource(this.state.selectedTab, this.props.searchTerm, index);
this.updatePageUrl(this.props.searchTerm, this.state.selectedTab, index);
};
......@@ -209,19 +207,14 @@ export class SearchPage extends React.Component<SearchPageProps, SearchPageState
<label>{ title }</label>
<InfoButton infoText={SEARCH_INFO_TEXT}/>
</div>
<SearchList results={ results.results } params={ {source: SEARCH_SOURCE_NAME, paginationStartIndex: 0 } }/>
<div className="search-pagination-component">
{
total_results > RESULTS_PER_PAGE &&
<Pagination
activePage={ page_index + 1 }
itemsCountPerPage={ RESULTS_PER_PAGE }
totalItemsCount={ total_results }
pageRangeDisplayed={ PAGINATION_PAGE_RANGE }
onChange={ this.onPaginationChange }
/>
}
</div>
<ResourceList
slicedItems={ results.results }
slicedItemsCount={ total_results }
source={ SEARCH_SOURCE_NAME }
itemsPerPage={ RESULTS_PER_PAGE }
activePage={ page_index }
onPagination={ this.onPaginationChange }
/>
</div>
);
};
......
import * as React from 'react';
import * as DocumentTitle from 'react-document-title';
import Pagination from 'react-js-pagination';
import { shallow } from 'enzyme';
......@@ -23,11 +22,11 @@ import InfoButton from 'components/common/InfoButton';
import TabsComponent from 'components/common/Tabs';
import SearchBar from '../SearchBar';
import SearchList from '../SearchList';
import globalState from 'fixtures/globalState';
import LoadingSpinner from 'components/common/LoadingSpinner';
import BookmarkList from 'components/common/Bookmark/BookmarkList';
import ResourceList from 'components/common/ResourceList';
import globalState from 'fixtures/globalState';
describe('SearchPage', () => {
const setStateSpy = jest.spyOn(SearchPage.prototype, 'setState');
......@@ -449,11 +448,11 @@ describe('SearchPage', () => {
});
it('calls props.searchResource with correct parameters', () => {
expect(searchResourceSpy).toHaveBeenCalledWith(wrapper.state().selectedTab, props.searchTerm, testIndex - 1);
expect(searchResourceSpy).toHaveBeenCalledWith(wrapper.state().selectedTab, props.searchTerm, testIndex);
});
it('calls updatePageUrl with correct parameters', () => {
expect(updatePageUrlSpy).toHaveBeenCalledWith(props.searchTerm, wrapper.state().selectedTab, testIndex - 1);
expect(updatePageUrlSpy).toHaveBeenCalledWith(props.searchTerm, wrapper.state().selectedTab, testIndex);
});
});
......@@ -563,21 +562,7 @@ describe('SearchPage', () => {
});
});
it('renders SearchList with correct props', () => {
expect(content.children().find(SearchList).props()).toMatchObject({
results: props.tables.results,
params: {
source: SEARCH_SOURCE_NAME,
paginationStartIndex: 0,
},
});
});
it('does not render Pagination if total_results <= RESULTS_PER_PAGE', () => {
expect(content.children().find(Pagination).exists()).toBeFalsy()
});
it('renders Pagination with correct props if total_results > RESULTS_PER_PAGE', () => {
it('renders ResourceList with correct props', () => {
const { props, wrapper } = setup();
const testResults = {
page_index: 0,
......@@ -585,12 +570,14 @@ describe('SearchPage', () => {
total_results: 11,
};
content = shallow(wrapper.instance().getTabContent(testResults, TABLE_RESOURCE_TITLE));
expect(content.children().find(Pagination).props()).toMatchObject({
activePage: 1,
itemsCountPerPage: RESULTS_PER_PAGE,
totalItemsCount: 11,
pageRangeDisplayed: PAGINATION_PAGE_RANGE,
onChange: wrapper.instance().onPaginationChange,
expect(content.children().find(ResourceList).props()).toMatchObject({
activePage: 0,
slicedItems: testResults.results,
slicedItemsCount: testResults.total_results,
itemsPerPage: RESULTS_PER_PAGE,
onPagination: wrapper.instance().onPaginationChange,
source: SEARCH_SOURCE_NAME,
});
});
});
......
export const ITEMS_PER_PAGE = 4;
export const PAGINATION_PAGE_RANGE = 10;
export const BOOKMARK_TITLE = "My Bookmarks";
export const BOOKMARKS_PER_PAGE = 4;
export const EMPTY_BOOKMARK_MESSAGE = "You don't have any bookmarks. Use the star icon to save a bookmark.";
export const MY_BOOKMARKS_SOURCE_NAME = "bookmarks";
import * as React from 'react';
import { connect } from 'react-redux';
import Pagination from 'react-js-pagination';
import { GlobalState } from "ducks/rootReducer";
import { GlobalState } from 'ducks/rootReducer';
import './styles.scss'
import { Bookmark } from 'interfaces';
import ResourceListItem from "components/common/ResourceListItem";
import {
ITEMS_PER_PAGE,
EMPTY_BOOKMARK_MESSAGE,
PAGINATION_PAGE_RANGE,
BOOKMARK_TITLE,
} from "./constants";
BOOKMARKS_PER_PAGE,
EMPTY_BOOKMARK_MESSAGE,
MY_BOOKMARKS_SOURCE_NAME,
} from './constants';
import ResourceList from 'components/common/ResourceList';
interface StateFromProps {
myBookmarks: Bookmark[];
isLoaded: boolean;
}
export type BookmarkListProps = StateFromProps;
interface BookmarkListState {
activePage: number;
}
export type MyBookmarksProps = StateFromProps;
export class BookmarkList extends React.Component<BookmarkListProps, BookmarkListState> {
export class MyBookmarks extends React.Component<MyBookmarksProps> {
constructor(props) {
super(props);
this.state = { activePage: 0 };
}
onPaginationChange = (pageNumber: number) => {
const index = pageNumber - 1;
this.setState({ activePage: index });
};
render() {
if (!this.props.isLoaded) {
return null;
}
const totalBookmarks = this.props.myBookmarks.length;
const startIndex = this.state.activePage * ITEMS_PER_PAGE;
const displayedBookmarks = this.props.myBookmarks.slice(startIndex, startIndex + ITEMS_PER_PAGE);
if (totalBookmarks === 0) {
return (
<div className="bookmark-list">
<div className="title-1">{ BOOKMARK_TITLE }</div>
<div className="empty-message body-placeholder">
{ EMPTY_BOOKMARK_MESSAGE }
</div>
</div>
)
}
const bookmarksLength = this.props.myBookmarks.length;
return (
<div className="bookmark-list">
<div className="title-1">{ BOOKMARK_TITLE }</div>
<ul className="list-group">
{
displayedBookmarks.map((resource, index) => {
const logging = { index: index + this.state.activePage * ITEMS_PER_PAGE , source: 'Bookmarks' };
return <ResourceListItem item={ resource } logging={ logging } key={ index } />;
})
}
</ul>
{
totalBookmarks > ITEMS_PER_PAGE &&
<div className="pagination-container">
<Pagination
activePage={ this.state.activePage + 1 }
itemsCountPerPage={ ITEMS_PER_PAGE }
totalItemsCount={ totalBookmarks }
pageRangeDisplayed={ PAGINATION_PAGE_RANGE }
onChange={ this.onPaginationChange }
/>
bookmarksLength === 0 &&
<div className="empty-message body-placeholder">
{ EMPTY_BOOKMARK_MESSAGE }
</div>
}
{
bookmarksLength !== 0 &&
<ResourceList
allItems={ this.props.myBookmarks }
source={ MY_BOOKMARKS_SOURCE_NAME }
itemsPerPage={ BOOKMARKS_PER_PAGE }
/>
}
</div>
)
);
}
}
......@@ -91,4 +60,4 @@ export const mapStateToProps = (state: GlobalState) => {
};
};
export default connect<StateFromProps>(mapStateToProps)(BookmarkList);
export default connect<StateFromProps>(mapStateToProps)(MyBookmarks);
......@@ -7,8 +7,7 @@
margin-bottom: 32px;
}
.empty-message,
.pagination-container {
.empty-message {
display: flex;
justify-content: center;
}
......
......@@ -4,20 +4,20 @@ import { shallow } from 'enzyme';
import globalState from 'fixtures/globalState';
import { ResourceType } from 'interfaces';
import Pagination from 'react-js-pagination';
import ResourceListItem from 'components/common/ResourceListItem'
import { BookmarkList, BookmarkListProps, mapStateToProps } from "../";
import { MyBookmarks, MyBookmarksProps, mapStateToProps } from '../';
import ResourceList from 'components/common/ResourceList';
import {
ITEMS_PER_PAGE,
EMPTY_BOOKMARK_MESSAGE,
BOOKMARK_TITLE,
BOOKMARKS_PER_PAGE,
EMPTY_BOOKMARK_MESSAGE,
MY_BOOKMARKS_SOURCE_NAME,
} from '../constants';
describe('BookmarkList', () => {
const setStateSpy = jest.spyOn(BookmarkList.prototype, 'setState');
describe('MyBookmarks', () => {
const setStateSpy = jest.spyOn(MyBookmarks.prototype, 'setState');
const setup = (propOverrides?: Partial<BookmarkListProps>) => {
const props: BookmarkListProps = {
const setup = (propOverrides?: Partial<MyBookmarksProps>) => {
const props: MyBookmarksProps = {
myBookmarks: [
{
key: 'bookmark-1',
......@@ -77,20 +77,10 @@ describe('BookmarkList', () => {
isLoaded: true,
...propOverrides
};
const wrapper = shallow<BookmarkList>(<BookmarkList {...props} />);
const wrapper = shallow<MyBookmarks>(<MyBookmarks {...props} />);
return { props, wrapper };
};
describe('onPaginationChange', () => {
it('reduces the page index by 1 and updates state', () => {
const { props, wrapper } = setup();
const pageNumber = 3;
wrapper.instance().onPaginationChange(pageNumber);
expect(setStateSpy).toHaveBeenCalledWith({ activePage: pageNumber - 1 });
});
});
describe('Render', () => {
it('Renders nothing until ready', () => {
const { props, wrapper } = setup({ isLoaded: false });
......@@ -107,29 +97,14 @@ describe('BookmarkList', () => {
expect(wrapper.find('.empty-message').text()).toEqual(EMPTY_BOOKMARK_MESSAGE);
});
it('Renders at most ITEMS_PER_PAGE bookmarks at once', () => {
const { props, wrapper } = setup();
expect(wrapper.find(ResourceListItem).length).toEqual(ITEMS_PER_PAGE)
});
it('Renders a pagination widget when there are more than ITEMS_PER_PAGE bookmarks', () => {
it('Renders ResourceList with the correct props', () => {
const { props, wrapper } = setup();
expect(wrapper.find(Pagination).exists()).toBe(true)
});
it('Hides a pagination widget when there are fewer than ITEMS_PER_PAGE bookmarks', () => {
const { props, wrapper } = setup({
myBookmarks: [{
key: 'bookmark-1',
type: ResourceType.table,
cluster: 'cluster',
database: 'database',
description: 'description',
name: 'name',
schema_name: 'schema_name',
}]
expect(wrapper.children().find(ResourceList).props()).toMatchObject({
allItems: props.myBookmarks,
itemsPerPage: BOOKMARKS_PER_PAGE,
source: MY_BOOKMARKS_SOURCE_NAME,
});
expect(wrapper.find(Pagination).exists()).toBe(false)
});
});
});
......
......@@ -2,3 +2,4 @@
export const POPULAR_TABLES_INFO_TEXT = 'These are some of the most commonly accessed tables within your organization.';
export const POPULAR_TABLES_LABEL = 'Popular Tables';
export const POPULAR_TABLES_SOURCE_NAME = 'popular_tables';
export const POPULAR_TABLES_PER_PAGE = 4;
......@@ -3,9 +3,14 @@ import * as React from 'react';
// TODO: Use css-modules instead of 'import'
import './styles.scss';
import { POPULAR_TABLES_LABEL, POPULAR_TABLES_INFO_TEXT, POPULAR_TABLES_SOURCE_NAME } from './constants';
import {
POPULAR_TABLES_LABEL,
POPULAR_TABLES_INFO_TEXT,
POPULAR_TABLES_SOURCE_NAME,
POPULAR_TABLES_PER_PAGE
} from './constants';
import InfoButton from 'components/common/InfoButton';
import SearchList from 'components/SearchPage/SearchList';
import ResourceList from 'components/common/ResourceList';
import { getPopularTables } from 'ducks/popularTables/reducer';
import { GetPopularTablesRequest, TableResource } from 'ducks/popularTables/types';
......@@ -39,12 +44,10 @@ export class PopularTables extends React.Component<PopularTablesProps> {
<label className="title-1">{POPULAR_TABLES_LABEL}</label>
<InfoButton infoText={POPULAR_TABLES_INFO_TEXT} />
</div>
<SearchList
results={this.props.popularTables}
params={{
source: POPULAR_TABLES_SOURCE_NAME,
paginationStartIndex: 0,
}}
<ResourceList
allItems={ this.props.popularTables }
source={ POPULAR_TABLES_SOURCE_NAME }
itemsPerPage={ POPULAR_TABLES_PER_PAGE }
/>
</>
);
......
......@@ -4,10 +4,11 @@ import { shallow } from 'enzyme';
import {
POPULAR_TABLES_INFO_TEXT,
POPULAR_TABLES_LABEL,
POPULAR_TABLES_PER_PAGE,
POPULAR_TABLES_SOURCE_NAME,
} from '../constants';
import InfoButton from 'components/common/InfoButton';
import SearchList from 'components/SearchPage/SearchList';
import ResourceList from 'components/common/ResourceList';
import globalState from 'fixtures/globalState';
import { PopularTables, PopularTablesProps, mapStateToProps, mapDispatchToProps } from '..';
......@@ -80,14 +81,12 @@ describe('PopularTables', () => {
});
});
it('renders SearchList with correct props', () => {
expect(wrapper.children().find(SearchList).props()).toMatchObject({
results: props.popularTables,
params: {
source: POPULAR_TABLES_SOURCE_NAME,
paginationStartIndex: 0,
},
it('renders ResourceList with correct props', () => {
expect(wrapper.children().find(ResourceList).props()).toMatchObject({
allItems: props.popularTables,
itemsPerPage: POPULAR_TABLES_PER_PAGE,
source: POPULAR_TABLES_SOURCE_NAME,
});
});
});
});
\ No newline at end of file
});
export const ITEMS_PER_PAGE = 4;
export const PAGINATION_PAGE_RANGE = 10;
import * as React from 'react';
import Pagination from 'react-js-pagination';
import ResourceListItem from 'components/common/ResourceListItem';
import { Resource } from 'interfaces';
import { ITEMS_PER_PAGE, PAGINATION_PAGE_RANGE } from './constants';
export interface ResourceListProps {
source: string;
paginate?: boolean;
itemsPerPage?: number;
// Choose to use either 'allItems' vs 'slicedItems' depending on if you're passing the entire list
// of items vs a pre-sliced section of all items.
allItems?: Resource[];
// 'slicedItems' and 'slicedItemsCount' should be used together
slicedItems?: Resource[];
slicedItemsCount?: number;
// 'onPagination' and 'activePage' should be used together
onPagination?: (pageNumber: number) => void;
activePage?: number;
}
interface ResourceListState {
activePage: number;
}
class ResourceList extends React.Component<ResourceListProps, ResourceListState> {
public static defaultProps: Partial<ResourceListProps> = {
paginate: true,
itemsPerPage: ITEMS_PER_PAGE,
};
constructor(props) {
super(props);
this.state = { activePage: 0 };
}
onPagination = (rawPageNum: number) => {
const activePage = rawPageNum - 1;
if (this.props.onPagination !== undefined) {
// activePage is managed externally via 'props'
this.props.onPagination(activePage);
} else {
// activePage is managed internally via 'state'.
this.setState({ activePage });
}
};
render() {
const { allItems, slicedItems, itemsPerPage, paginate, source } = this.props;
const activePage = this.props.activePage !== undefined ? this.props.activePage : this.state.activePage;
const itemsCount = this.props.slicedItemsCount || allItems.length;
const startIndex = itemsPerPage * activePage;
let itemsToRender = slicedItems || allItems;
if (paginate && allItems) {
itemsToRender = allItems.slice(startIndex, startIndex + itemsPerPage);
}
return (
<>
<ul className="list-group">
{
itemsToRender.map((item, idx) => {
const logging = { source, index: startIndex + idx };
return <ResourceListItem item={ item } logging={ logging } key={ idx } />;
})
}
</ul>
{
paginate &&
itemsCount > itemsPerPage &&
<div className="text-center">
<Pagination
activePage={ activePage + 1 }
itemsCountPerPage={ itemsPerPage }
totalItemsCount={ itemsCount }
pageRangeDisplayed={ PAGINATION_PAGE_RANGE }
onChange={ this.onPagination }
/>
</div>
}
</>
);
}
}
export default ResourceList;
import * as React from 'react';
import Pagination from 'react-js-pagination';
import { shallow } from 'enzyme';
import { ResourceType } from 'interfaces';
import ResourceListItem from 'components/common/ResourceListItem/index';
import ResourceList, { ResourceListProps } from '../';
describe('ResourceList', () => {
const setStateSpy = jest.spyOn(ResourceList.prototype, 'setState');
const setupAllItems = (propOverrides?: Partial<ResourceListProps>) => {
const props: ResourceListProps = {
allItems: [
{ type: ResourceType.table },
{ type: ResourceType.table },
{ type: ResourceType.table },
{ type: ResourceType.table },
{ type: ResourceType.table },
{ type: ResourceType.table },
],
itemsPerPage: 4,
source: 'testSource',
...propOverrides
};
const wrapper = shallow<ResourceList>(<ResourceList {...props} />);
return { props, wrapper };
};
const setupSlicedItems = (propOverrides?: Partial<ResourceListProps>) => {
const props: ResourceListProps = {
source: 'testSource',
activePage: 3,
slicedItems: [
{ type: ResourceType.table },
{ type: ResourceType.table },
{ type: ResourceType.table },
{ type: ResourceType.table },
],
slicedItemsCount: 40,
itemsPerPage: 4,
onPagination: jest.fn(),
...propOverrides
};
const wrapper = shallow<ResourceList>(<ResourceList {...props} />);
return { props, wrapper };
};
describe('render with no pagination', () => {
let props;
let wrapper;
beforeAll(() => {
const setupResult = setupAllItems({ paginate: false });
props = setupResult.props;
wrapper = setupResult.wrapper;
});
it('should render all items', () => {
const items = wrapper.find(ResourceListItem);
expect(items.length).toEqual(props.allItems.length);
});
it('passes correct props to each ResourceListItem', () => {
const items = wrapper.find(ResourceListItem);
items.forEach((contentItem, idx) => {
expect(contentItem.props()).toMatchObject({
item: props.allItems[idx],
logging: {
source: props.source,
index: idx,
}
})
});
});
});
describe('render allItems', () => {
let props;
let wrapper;
beforeAll(() => {
const setupResult = setupAllItems();
props = setupResult.props;
wrapper = setupResult.wrapper;
});
it('renders at most itemsPerPage ResourceListItems', () => {
const items = wrapper.find(ResourceListItem);
expect(items.length).toEqual(props.itemsPerPage);
});
it('renders a pagination widget when there are more than itemsPerPage items', () => {
expect(wrapper.find(Pagination).exists()).toBe(true)
});
it('hides a pagination widget when there are fewer than itemsPerPage items', () => {
const { props, wrapper } = setupAllItems({
itemsPerPage: 20,
});
expect(wrapper.find(Pagination).exists()).toBe(false)
});
});
describe('render slicedItems', () => {
let props;
let wrapper;
beforeAll(() => {
const setupResult = setupSlicedItems();
props = setupResult.props;
wrapper = setupResult.wrapper;
});
it('renders all slicedItems', () => {
const items = wrapper.find(ResourceListItem);
expect(items.length).toEqual(props.slicedItems.length);
});
it('renders pagination widget when slicedItemsCount is greater than itemsPerPage', () => {
expect(wrapper.find(Pagination).exists()).toBe(true);
});
it('hides pagination widget when slicedItemsCount is less than itemsPerPage', () => {
const { props, wrapper } = setupAllItems({
itemsPerPage: 20,
});
expect(wrapper.find(Pagination).exists()).toBe(false);
});
});
describe('onPagination', () => {
it('calls the onPagination prop when it exists', () => {
const setupResult = setupSlicedItems();
const wrapper = setupResult.wrapper;
const props = setupResult.props;
const onPaginationSpy = jest.spyOn(props, 'onPagination');
wrapper.instance().onPagination(3);
expect(onPaginationSpy).toHaveBeenCalledWith(2);
});
it('calls setState when onPagination prop does not exist', () => {
setStateSpy.mockClear();
const setupResult = setupSlicedItems({ onPagination: undefined });
const wrapper = setupResult.wrapper;
wrapper.instance().onPagination(3);
expect(setStateSpy).toHaveBeenCalledWith({ activePage: 2 });
});
});
});
......@@ -5,7 +5,7 @@ import { LoggingParams } from '../types';
import { TableResource } from 'interfaces';
import BookmarkIcon from "components/common/Bookmark/BookmarkIcon";
import BookmarkIcon from 'components/common/Bookmark/BookmarkIcon';
export interface TableListItemProps {
table: TableResource;
......
......@@ -13,7 +13,7 @@ export interface ListItemProps {
item: Resource;
}
export default class ResourceListItem extends React.Component<ListItemProps, {}> {
export default class ResourceListItem extends React.Component<ListItemProps> {
constructor(props) {
super(props);
}
......
......@@ -16,7 +16,7 @@ import Footer from './components/Footer';
import HomePage from './components/HomePage'
import NavBar from './components/NavBar';
import NotFoundPage from './components/NotFoundPage';
import Preloader from "components/common/Preloader";
import Preloader from 'components/common/Preloader';
import ProfilePage from './components/ProfilePage';
import SearchPage from './components/SearchPage';
import TableDetail from './components/TableDetail';
......
......@@ -1074,13 +1074,15 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz",
"integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=",
"dev": true
"dev": true,
"optional": true
},
"is-glob": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz",
"integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=",
"dev": true,
"optional": true,
"requires": {
"is-extglob": "^1.0.0"
}
......@@ -4947,7 +4949,8 @@
"ansi-regex": {
"version": "2.1.1",
"resolved": false,
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
"optional": true
},
"aproba": {
"version": "1.2.0",
......@@ -4968,12 +4971,14 @@
"balanced-match": {
"version": "1.0.0",
"resolved": false,
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
"optional": true
},
"brace-expansion": {
"version": "1.1.11",
"resolved": false,
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"optional": true,
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
......@@ -4988,17 +4993,20 @@
"code-point-at": {
"version": "1.1.0",
"resolved": false,
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
"optional": true
},
"concat-map": {
"version": "0.0.1",
"resolved": false,
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
"optional": true
},
"console-control-strings": {
"version": "1.1.0",
"resolved": false,
"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4="
"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
"optional": true
},
"core-util-is": {
"version": "1.0.2",
......@@ -5115,7 +5123,8 @@
"inherits": {
"version": "2.0.3",
"resolved": false,
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
"optional": true
},
"ini": {
"version": "1.3.5",
......@@ -5127,6 +5136,7 @@
"version": "1.0.0",
"resolved": false,
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
"optional": true,
"requires": {
"number-is-nan": "^1.0.0"
}
......@@ -5141,6 +5151,7 @@
"version": "3.0.4",
"resolved": false,
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"optional": true,
"requires": {
"brace-expansion": "^1.1.7"
}
......@@ -5148,12 +5159,14 @@
"minimist": {
"version": "0.0.8",
"resolved": false,
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
"optional": true
},
"minipass": {
"version": "2.2.4",
"resolved": false,
"integrity": "sha512-hzXIWWet/BzWhYs2b+u7dRHlruXhwdgvlTMDKC6Cb1U7ps6Ac6yQlR39xsbjWJE377YTCtKwIXIpJ5oP+j5y8g==",
"optional": true,
"requires": {
"safe-buffer": "^5.1.1",
"yallist": "^3.0.0"
......@@ -5172,6 +5185,7 @@
"version": "0.5.1",
"resolved": false,
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
"optional": true,
"requires": {
"minimist": "0.0.8"
}
......@@ -5252,7 +5266,8 @@
"number-is-nan": {
"version": "1.0.1",
"resolved": false,
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0="
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
"optional": true
},
"object-assign": {
"version": "4.1.1",
......@@ -5264,6 +5279,7 @@
"version": "1.4.0",
"resolved": false,
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"optional": true,
"requires": {
"wrappy": "1"
}
......@@ -5349,7 +5365,8 @@
"safe-buffer": {
"version": "5.1.1",
"resolved": false,
"integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg=="
"integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==",
"optional": true
},
"safer-buffer": {
"version": "2.1.2",
......@@ -5385,6 +5402,7 @@
"version": "1.0.2",
"resolved": false,
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
"optional": true,
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",
......@@ -5404,6 +5422,7 @@
"version": "3.0.1",
"resolved": false,
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"optional": true,
"requires": {
"ansi-regex": "^2.0.0"
}
......@@ -5447,12 +5466,14 @@
"wrappy": {
"version": "1.0.2",
"resolved": false,
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
"optional": true
},
"yallist": {
"version": "3.0.2",
"resolved": false,
"integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k="
"integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k=",
"optional": true
}
}
},
......@@ -11782,7 +11803,7 @@
"react-avatar": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/react-avatar/-/react-avatar-2.5.1.tgz",
"integrity": "sha512-bwH5pWY6uxaKZt+IZBfD+SU3Dpy3FaKbmAzrOI4N8SATUPLXOdGaJHWUl6Vl8hHSwWSsoLh/m7xYHdnn0lofZw==",
"integrity": "sha1-W76MHQpSWT1GCPs9hinamV7rcJU=",
"requires": {
"babel-runtime": ">=5.0.0",
"is-retina": "^1.0.3",
......
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