From 4ffdd23d09532f0be903cb51e537ab4adb14a2c7 Mon Sep 17 00:00:00 2001 From: Martin Marosi Date: Thu, 11 Jul 2019 10:15:37 +0200 Subject: [PATCH 1/4] Added portfolios pagination meta data to redux storage. --- src/redux/actions/portfolio-actions.js | 2 +- src/redux/reducers/portfolio-reducer.js | 2 +- .../async-pagination.js} | 25 +++--- .../portfolio/add-portfolio-modal.js | 2 +- .../add-products-to-portfolio/index.js | 3 +- .../copy-portfolio-item-modal.js | 2 +- src/smart-components/portfolio/portfolios.js | 5 +- .../portfolio/remove-portfolio-modal.js | 2 +- .../portfolio/share-portfolio-modal.js | 2 +- .../redux/actions/portfolio-actions.test.js | 2 +- .../portfolio/add-portfolio-modal.test.js | 4 +- .../add-products-pagination.test.js.snap | 12 --- .../add-products-pagination.test.js | 78 ------------------- .../portfolio/portfolio.test.js | 4 +- .../portfolio/portfolios.test.js | 5 +- .../portfolio/remove-portfolio-modal.test.js | 6 +- .../portfolio/share-portfolio-modal.test.js | 4 +- .../schemas/add-products-toolbar.schema.js | 10 ++- 18 files changed, 39 insertions(+), 131 deletions(-) rename src/smart-components/{portfolio/add-products-to-portfolio/add-products-pagination.js => common/async-pagination.js} (53%) delete mode 100644 src/test/smart-components/portfolio/add-products-to-portfolio/__snapshots__/add-products-pagination.test.js.snap delete mode 100644 src/test/smart-components/portfolio/add-products-to-portfolio/add-products-pagination.test.js diff --git a/src/redux/actions/portfolio-actions.js b/src/redux/actions/portfolio-actions.js index 5ff0c1441..45747f8c4 100644 --- a/src/redux/actions/portfolio-actions.js +++ b/src/redux/actions/portfolio-actions.js @@ -7,7 +7,7 @@ import * as PortfolioHelper from '../../helpers/portfolio/portfolio-helper'; export const doFetchPortfolios = apiProps => ({ type: ActionTypes.FETCH_PORTFOLIOS, - payload: PortfolioHelper.listPortfolios(apiProps).then(({ data }) => data) + payload: PortfolioHelper.listPortfolios(apiProps) }); export const fetchPortfolios = apiProps => (dispatch) => dispatch(doFetchPortfolios(apiProps)); diff --git a/src/redux/reducers/portfolio-reducer.js b/src/redux/reducers/portfolio-reducer.js index 79a8d3cd0..1a7551431 100644 --- a/src/redux/reducers/portfolio-reducer.js +++ b/src/redux/reducers/portfolio-reducer.js @@ -15,7 +15,7 @@ import { export const portfoliosInitialState = { portfolioItems: [], portfolioItem: {}, - portfolios: [], + portfolios: { data: []}, portfolio: {}, filterValue: '', isLoading: false diff --git a/src/smart-components/portfolio/add-products-to-portfolio/add-products-pagination.js b/src/smart-components/common/async-pagination.js similarity index 53% rename from src/smart-components/portfolio/add-products-to-portfolio/add-products-pagination.js rename to src/smart-components/common/async-pagination.js index cc20f3bd5..b36bc6676 100644 --- a/src/smart-components/portfolio/add-products-to-portfolio/add-products-pagination.js +++ b/src/smart-components/common/async-pagination.js @@ -1,17 +1,14 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; -import { bindActionCreators } from 'redux'; import debouncePromise from 'awesome-debounce-promise'; import { Pagination } from '@redhat-cloud-services/frontend-components'; -import { getCurrentPage, getNewPage } from '../../../helpers/shared/pagination'; -import { fetchPlatformItems } from '../../../redux/actions/platform-actions'; +import { getCurrentPage, getNewPage } from '../../helpers/shared/pagination'; -const AddProductsPagination = ({ meta: { limit, count, offset }, platformId, fetchPlatformItems }) => { +const AsyncPagination = ({ meta: { limit, count, offset }, apiProps, apiRequest }) => { - const handleOnPerPageSelect = limit => fetchPlatformItems(platformId, { + const handleOnPerPageSelect = limit => apiRequest(apiProps, { offset, limit }); @@ -22,7 +19,7 @@ const AddProductsPagination = ({ meta: { limit, count, offset }, platformId, fet limit }; - const request = () => fetchPlatformItems(platformId, options); + const request = () => apiRequest(apiProps, options); if (debounce) { return debouncePromise(request, 250)(); } @@ -42,17 +39,17 @@ const AddProductsPagination = ({ meta: { limit, count, offset }, platformId, fet ); }; -AddProductsPagination.propTypes = { +AsyncPagination.propTypes = { meta: PropTypes.shape({ count: PropTypes.number.isRequired, limit: PropTypes.number.isRequired, offset: PropTypes.number.isRequired }), - fetchPlatformItems: PropTypes.func.isRequired, - platformId: PropTypes.string.isRequired + apiRequest: PropTypes.func.isRequired, + apiProps: PropTypes.any }; -AddProductsPagination.defaultProps = { +AsyncPagination.defaultProps = { meta: { count: 0, limit: 50, @@ -60,9 +57,5 @@ AddProductsPagination.defaultProps = { } }; -const mapDistapchToProps = dispatch => bindActionCreators({ - fetchPlatformItems -}, dispatch); - -export default connect(() => ({}), mapDistapchToProps)(AddProductsPagination); +export default AsyncPagination; diff --git a/src/smart-components/portfolio/add-portfolio-modal.js b/src/smart-components/portfolio/add-portfolio-modal.js index a3c35c339..e3b55590a 100644 --- a/src/smart-components/portfolio/add-portfolio-modal.js +++ b/src/smart-components/portfolio/add-portfolio-modal.js @@ -72,7 +72,7 @@ AddPortfolioModal.propTypes = { }; const mapStateToProps = ({ approvalReducer: { workflows }, portfolioReducer: { portfolios }}, { match: { params: { id }}}) => ({ - initialValues: id && portfolios.find(item => item.id === id), + initialValues: id && portfolios.data.find(item => item.id === id), portfolioId: id, workflows }); diff --git a/src/smart-components/portfolio/add-products-to-portfolio/index.js b/src/smart-components/portfolio/add-products-to-portfolio/index.js index c544112c7..5ea648f47 100644 --- a/src/smart-components/portfolio/add-products-to-portfolio/index.js +++ b/src/smart-components/portfolio/add-products-to-portfolio/index.js @@ -82,7 +82,8 @@ const AddProductsToPortfolio = ({ onClickAddToPortfolio: handleAddToPortfolio, meta, platformId: selectedPlatform && selectedPlatform.id, - searchValue + searchValue, + fetchPlatformItems }) } /> ({ - portfolios + portfolios: portfolios.data }); const mapDispatchToProps = dispatch => bindActionCreators({ diff --git a/src/smart-components/portfolio/portfolios.js b/src/smart-components/portfolio/portfolios.js index 77ab59bdf..05726cfb1 100644 --- a/src/smart-components/portfolio/portfolios.js +++ b/src/smart-components/portfolio/portfolios.js @@ -69,7 +69,8 @@ class Portfolios extends Component { /> ) } /> - );} + ); + } render() { return ( @@ -82,7 +83,7 @@ class Portfolios extends Component { } const mapStateToProps = ({ portfolioReducer: { portfolios, isLoading, filterValue }}) => ({ - portfolios, + portfolios: portfolios.data, isLoading, searchFilter: filterValue }); diff --git a/src/smart-components/portfolio/remove-portfolio-modal.js b/src/smart-components/portfolio/remove-portfolio-modal.js index 479ef0e2d..145bf1288 100644 --- a/src/smart-components/portfolio/remove-portfolio-modal.js +++ b/src/smart-components/portfolio/remove-portfolio-modal.js @@ -67,7 +67,7 @@ RemovePortfolioModal.propTypes = { }; const portfolioDetailsFromState = (state, id) => - state.portfolioReducer.portfolios.find(portfolio => portfolio.id === id); + state.portfolioReducer.portfolios.data.find(portfolio => portfolio.id === id); const mapStateToProps = (state, { match: { params: { id }}}) => ({ portfolio: portfolioDetailsFromState(state, id) }); diff --git a/src/smart-components/portfolio/share-portfolio-modal.js b/src/smart-components/portfolio/share-portfolio-modal.js index d1d1ce6e2..d450440e9 100644 --- a/src/smart-components/portfolio/share-portfolio-modal.js +++ b/src/smart-components/portfolio/share-portfolio-modal.js @@ -156,7 +156,7 @@ const mapStateToProps = ({ rbacReducer: { rbacGroups }, portfolioReducer: { portfolios }, shareReducer: { shareInfo, isLoading }}, { match: { params: { id }}}) => ({ - initialValues: id && portfolios.find(item => item.id === id), + initialValues: id && portfolios.data.find(item => item.id === id), portfolioId: id, isLoading, shareInfo, diff --git a/src/test/redux/actions/portfolio-actions.test.js b/src/test/redux/actions/portfolio-actions.test.js index 3cbfd3196..f610c5b28 100644 --- a/src/test/redux/actions/portfolio-actions.test.js +++ b/src/test/redux/actions/portfolio-actions.test.js @@ -46,7 +46,7 @@ describe('Portfolio actions', () => { type: `${FETCH_PORTFOLIOS}_PENDING` }, { type: `${FETCH_PORTFOLIOS}_FULFILLED`, - payload: [ expectedPortfolio ] + payload: { data: [ expectedPortfolio ]} }]; apiClientMock.get(CATALOG_API_BASE + '/portfolios', mockOnce({ body: { data: [ expectedPortfolio ]} diff --git a/src/test/smart-components/portfolio/add-portfolio-modal.test.js b/src/test/smart-components/portfolio/add-portfolio-modal.test.js index 80f10317b..1ac2a008a 100644 --- a/src/test/smart-components/portfolio/add-portfolio-modal.test.js +++ b/src/test/smart-components/portfolio/add-portfolio-modal.test.js @@ -39,10 +39,10 @@ describe('', () => { }] }, portfolioReducer: { - portfolios: [{ + portfolios: { data: [{ id: '123', name: 'Portfolio' - }] + }]} } }; mockStore = configureStore(middlewares); diff --git a/src/test/smart-components/portfolio/add-products-to-portfolio/__snapshots__/add-products-pagination.test.js.snap b/src/test/smart-components/portfolio/add-products-to-portfolio/__snapshots__/add-products-pagination.test.js.snap deleted file mode 100644 index 7d7f7b03a..000000000 --- a/src/test/smart-components/portfolio/add-products-to-portfolio/__snapshots__/add-products-pagination.test.js.snap +++ /dev/null @@ -1,12 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[` should render correctly 1`] = ` - -`; diff --git a/src/test/smart-components/portfolio/add-products-to-portfolio/add-products-pagination.test.js b/src/test/smart-components/portfolio/add-products-to-portfolio/add-products-pagination.test.js deleted file mode 100644 index f0bb5e469..000000000 --- a/src/test/smart-components/portfolio/add-products-to-portfolio/add-products-pagination.test.js +++ /dev/null @@ -1,78 +0,0 @@ -import React from 'react'; -import thunk from 'redux-thunk'; -import { mount, shallow } from 'enzyme'; -import { shallowToJson } from 'enzyme-to-json'; -import configureStore from 'redux-mock-store' ; -import promiseMiddleware from 'redux-promise-middleware'; -import { notificationsMiddleware } from '@redhat-cloud-services/frontend-components-notifications/'; - -import { TOPOLOGICAL_INVENTORY_API_BASE } from '../../../../utilities/constants'; -import AddProductsPagination from '../../../../smart-components/portfolio/add-products-to-portfolio/add-products-pagination'; - -describe('', () => { - let initialProps; - const middlewares = [ thunk, promiseMiddleware(), notificationsMiddleware() ]; - let mockStore; - beforeEach(() => { - initialProps = { - platformId: '999', - meta: { - count: 123, - limit: 50, - offset: 0 - } - }; - mockStore = configureStore(middlewares); - }); - - it('should render correctly', () => { - const store = mockStore({}); - const wrapper = shallow().dive(); - expect(shallowToJson(wrapper)).toMatchSnapshot(); - }); - - it('should correctly request next page', done => { - const store = mockStore({}); - const fetchPlatformItems = jest.fn(); - const wrapper = mount(); - const lastPageButton = wrapper.find('button').at(3); - apiClientMock.get(`${TOPOLOGICAL_INVENTORY_API_BASE}/sources/999/service_offerings?filter%5Barchived_at%5D%5Bnil%5D=&limit=50&offset=50`, - mockOnce((req, res) => - { - expect(req).toBeTruthy(); - done(); - return res.status(200).body({ - data: [], - meta: { - count: 123, - limit: 50, - offset: 123 - }} - ); - })); - - lastPageButton.simulate('click'); - }); - - it('should correctly request last page', done => { - const store = mockStore({}); - const fetchPlatformItems = jest.fn(); - const wrapper = mount(); - const lastPageButton = wrapper.find('button').last(); - apiClientMock.get(`${TOPOLOGICAL_INVENTORY_API_BASE}/sources/999/service_offerings?filter%5Barchived_at%5D%5Bnil%5D=&limit=50&offset=100`, - mockOnce((req, res) => { - expect(req).toBeTruthy(); - done(); - return res.status(200).body({ - data: [], - meta: { - count: 123, - limit: 50, - offset: 123 - } - }); - })); - - lastPageButton.simulate('click'); - }); -}); diff --git a/src/test/smart-components/portfolio/portfolio.test.js b/src/test/smart-components/portfolio/portfolio.test.js index cd2cfd224..c0f62147d 100644 --- a/src/test/smart-components/portfolio/portfolio.test.js +++ b/src/test/smart-components/portfolio/portfolio.test.js @@ -47,12 +47,12 @@ describe('', () => { id: '123', name: 'Foo' }, - portfolios: [{ + portfolios: { data: [{ id: '123', name: 'bar', description: 'description', modified: 'sometimes' - }] + }]} } }; mockStore = configureStore(middlewares); diff --git a/src/test/smart-components/portfolio/portfolios.test.js b/src/test/smart-components/portfolio/portfolios.test.js index c79142102..ee5bdb7a8 100644 --- a/src/test/smart-components/portfolio/portfolios.test.js +++ b/src/test/smart-components/portfolio/portfolios.test.js @@ -35,14 +35,14 @@ describe('', () => { }; initialState = { portfolioReducer: { - portfolios: [{ + portfolios: { data: [{ id: '123', name: 'bar', description: 'description', modified: 'sometimes', created_at: 'foo', owner: 'Owner' - }] + }]} } }; mockStore = configureStore(middlewares); @@ -87,6 +87,7 @@ describe('', () => { ); setImmediate(() => { + wrapper.update(); expect(wrapper.find(PortfolioCard)).toHaveLength(1); const filterInput = wrapper.find(FilterToolbarItem).first(); filterInput.props().onFilterChange('nothing'); diff --git a/src/test/smart-components/portfolio/remove-portfolio-modal.test.js b/src/test/smart-components/portfolio/remove-portfolio-modal.test.js index 7d7085ed1..7b2ee719e 100644 --- a/src/test/smart-components/portfolio/remove-portfolio-modal.test.js +++ b/src/test/smart-components/portfolio/remove-portfolio-modal.test.js @@ -32,10 +32,10 @@ describe('', () => { }; initialState = { portfolioReducer: { - portfolios: [{ + portfolios: { data: [{ id: '123', name: 'Foo' - }] + }]} } }; mockStore = configureStore(middlewares); @@ -88,7 +88,7 @@ describe('', () => { type: `${FETCH_PORTFOLIOS}_PENDING` }), expect.objectContaining({ type: `${FETCH_PORTFOLIOS}_FULFILLED`, - payload: [] + payload: { data: []} }) ]; wrapper.find('button').last().simulate('click'); diff --git a/src/test/smart-components/portfolio/share-portfolio-modal.test.js b/src/test/smart-components/portfolio/share-portfolio-modal.test.js index 5f4e65f4a..39480845c 100644 --- a/src/test/smart-components/portfolio/share-portfolio-modal.test.js +++ b/src/test/smart-components/portfolio/share-portfolio-modal.test.js @@ -34,10 +34,10 @@ describe(' { }; initialState = { portfolioReducer: { - portfolios: [{ + portfolios: { data: [{ id: '123', name: 'Portfolio 1' - }] + }]} }, shareReducer: { shareInfo: [{ diff --git a/src/toolbar/schemas/add-products-toolbar.schema.js b/src/toolbar/schemas/add-products-toolbar.schema.js index d82ea7627..7ecf0dbec 100644 --- a/src/toolbar/schemas/add-products-toolbar.schema.js +++ b/src/toolbar/schemas/add-products-toolbar.schema.js @@ -2,7 +2,7 @@ import { toolbarComponentTypes } from '../toolbar-mapper'; import { createSingleItemGroup, createLinkButton } from '../helpers'; import FilterSelect from '../../presentational-components/shared/filter-select'; import ButtonWithSpinner from '../../presentational-components/shared/button-with-spinner'; -import AddProductsPagination from '../../smart-components/portfolio/add-products-to-portfolio/add-products-pagination'; +import AsyncPagination from '../../smart-components/common/async-pagination'; const createAddProductsSchema = ({ options, @@ -15,7 +15,8 @@ const createAddProductsSchema = ({ portfolioRoute, onClickAddToPortfolio, meta, - platformId + platformId, + fetchPlatformItems }) => ({ fields: [{ component: toolbarComponentTypes.TOP_TOOLBAR, @@ -76,10 +77,11 @@ const createAddProductsSchema = ({ component: toolbarComponentTypes.LEVEL_ITEM, key: 'pagination-item', fields: meta ? [{ - component: AddProductsPagination, + component: AsyncPagination, key: 'add-products-pagination', meta, - platformId + apiProps: platformId, + apiRequest: fetchPlatformItems }] : [] }] }] From 28d7aa004ba4536eff8ff016cf66866774a745e5 Mon Sep 17 00:00:00 2001 From: Martin Marosi Date: Thu, 11 Jul 2019 11:36:10 +0200 Subject: [PATCH 2/4] Added pagination to portfolios screen. --- src/helpers/portfolio/portfolio-helper.js | 4 +- src/redux/actions/portfolio-actions.js | 8 ++- src/redux/reducers/portfolio-reducer.js | 2 +- src/smart-components/portfolio/portfolios.js | 35 +++++---- .../portfolios-filter-toolbar.test.js.snap | 72 +++++++++++-------- .../portfolios-filter-toolbar.test.js | 2 +- .../__snapshots__/portfolios.test.js.snap | 1 + .../portfolio/portfolios.test.js | 6 +- .../schemas/portfolios-toolbar.schema.js | 63 ++++++++++------ 9 files changed, 117 insertions(+), 76 deletions(-) diff --git a/src/helpers/portfolio/portfolio-helper.js b/src/helpers/portfolio/portfolio-helper.js index 8836d268b..8764046d8 100644 --- a/src/helpers/portfolio/portfolio-helper.js +++ b/src/helpers/portfolio/portfolio-helper.js @@ -5,8 +5,8 @@ const axiosInstance = getAxiosInstance(); const portfolioApi = getPortfolioApi(); const portfolioItemApi = getPortfolioItemApi(); -export function listPortfolios() { - return portfolioApi.listPortfolios(); +export function listPortfolios(_apiProps, { limit, offset, filter, ...options } = {}) { + return portfolioApi.listPortfolios(limit, offset, filter, options); } export function getPortfolioItems() { diff --git a/src/redux/actions/portfolio-actions.js b/src/redux/actions/portfolio-actions.js index 45747f8c4..2c16cb587 100644 --- a/src/redux/actions/portfolio-actions.js +++ b/src/redux/actions/portfolio-actions.js @@ -5,12 +5,14 @@ import { ADD_NOTIFICATION, CLEAR_NOTIFICATIONS } from '@redhat-cloud-services/fr import * as ActionTypes from '../action-types'; import * as PortfolioHelper from '../../helpers/portfolio/portfolio-helper'; -export const doFetchPortfolios = apiProps => ({ +export const doFetchPortfolios = (...args) => ({ type: ActionTypes.FETCH_PORTFOLIOS, - payload: PortfolioHelper.listPortfolios(apiProps) + payload: PortfolioHelper.listPortfolios(...args) }); -export const fetchPortfolios = apiProps => (dispatch) => dispatch(doFetchPortfolios(apiProps)); +export const fetchPortfolios = (...args) => (dispatch) => { + return dispatch(doFetchPortfolios(...args)); +}; export const fetchPortfolioItems = apiProps => ({ type: ActionTypes.FETCH_PORTFOLIO_ITEMS, diff --git a/src/redux/reducers/portfolio-reducer.js b/src/redux/reducers/portfolio-reducer.js index 1a7551431..fbc509efc 100644 --- a/src/redux/reducers/portfolio-reducer.js +++ b/src/redux/reducers/portfolio-reducer.js @@ -15,7 +15,7 @@ import { export const portfoliosInitialState = { portfolioItems: [], portfolioItem: {}, - portfolios: { data: []}, + portfolios: { data: [], meta: {}}, portfolio: {}, filterValue: '', isLoading: false diff --git a/src/smart-components/portfolio/portfolios.js b/src/smart-components/portfolio/portfolios.js index 05726cfb1..54748967d 100644 --- a/src/smart-components/portfolio/portfolios.js +++ b/src/smart-components/portfolio/portfolios.js @@ -1,6 +1,7 @@ import React, { Component, Fragment } from 'react'; -import propTypes from 'prop-types'; +import PropTypes from 'prop-types'; import { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; import { Route, Switch } from 'react-router-dom'; import { SearchIcon } from '@patternfly/react-icons'; @@ -11,6 +12,7 @@ import RemovePortfolio from './remove-portfolio-modal'; import { scrollToTop } from '../../helpers/shared/helpers'; import ToolbarRenderer from '../../toolbar/toolbar-renderer'; import ContentGallery from '../content-gallery/content-gallery'; +import { defaultSettings } from '../../helpers/shared/pagination'; import { fetchPortfolios } from '../../redux/actions/portfolio-actions'; import PortfolioCard from '../../presentational-components/portfolio/porfolio-card'; import createPortfolioToolbarSchema from '../../toolbar/schemas/portfolios-toolbar.schema'; @@ -29,7 +31,7 @@ class Portfolios extends Component { }; fetchData = () => { - this.props.fetchPortfolios(); + this.props.fetchPortfolios(undefined, defaultSettings); }; componentDidMount() { @@ -44,12 +46,14 @@ class Portfolios extends Component { items: this.props.portfolios .filter(({ name }) => name.toLowerCase().includes(this.state.filterValue.trim().toLowerCase())) .map(item => ), - isLoading: this.props.isLoading && this.props.portfolios.length === 0 + isLoading: this.props.isLoading }; return ( ({ portfolios: portfolios.data, + pagination: portfolios.meta, isLoading, searchFilter: filterValue }); -const mapDispatchToProps = dispatch => ({ - fetchPortfolios: apiProps => dispatch(fetchPortfolios(apiProps)) -}); +const mapDispatchToProps = dispatch => bindActionCreators({ + fetchPortfolios +}, dispatch); Portfolios.propTypes = { - filteredItems: propTypes.array, - portfolios: propTypes.array, - platforms: propTypes.array, - isLoading: propTypes.bool, - searchFilter: propTypes.string, - showModal: propTypes.func, - fetchPortfolios: propTypes.func.isRequired + filteredItems: PropTypes.array, + portfolios: PropTypes.array, + platforms: PropTypes.array, + isLoading: PropTypes.bool, + searchFilter: PropTypes.string, + showModal: PropTypes.func, + fetchPortfolios: PropTypes.func.isRequired, + pagination: PropTypes.object }; Portfolios.defaultProps = { - portfolios: [] + portfolios: [], + pagination: {} }; export default connect(mapStateToProps, mapDispatchToProps)(Portfolios); diff --git a/src/test/presentational-components/portfolio/__snapshots__/portfolios-filter-toolbar.test.js.snap b/src/test/presentational-components/portfolio/__snapshots__/portfolios-filter-toolbar.test.js.snap index 734d53d8c..35f02f929 100644 --- a/src/test/presentational-components/portfolio/__snapshots__/portfolios-filter-toolbar.test.js.snap +++ b/src/test/presentational-components/portfolio/__snapshots__/portfolios-filter-toolbar.test.js.snap @@ -30,50 +30,64 @@ exports[` should render correctly 1`] = ` title="Portfolios" /> + component="ToolbarItem" + key="filter-group/single-toolbar-item" + > + + - - + className="" + component="Link" + key="create-portfolio-button/button-link" + to="/portfolios/add-portfolio" + > + + + + + diff --git a/src/test/presentational-components/portfolio/portfolios-filter-toolbar.test.js b/src/test/presentational-components/portfolio/portfolios-filter-toolbar.test.js index cb0933b53..6c1a75305 100644 --- a/src/test/presentational-components/portfolio/portfolios-filter-toolbar.test.js +++ b/src/test/presentational-components/portfolio/portfolios-filter-toolbar.test.js @@ -35,7 +35,7 @@ describe('', () => { ); - const input = wrapper.find('input'); + const input = wrapper.find('input').first(); input.getDOMNode.value = 'foo'; input.simulate('change'); expect(onFilterChange).toHaveBeenCalled(); diff --git a/src/test/smart-components/portfolio/__snapshots__/portfolios.test.js.snap b/src/test/smart-components/portfolio/__snapshots__/portfolios.test.js.snap index cb0cd52a3..ee00112d2 100644 --- a/src/test/smart-components/portfolio/__snapshots__/portfolios.test.js.snap +++ b/src/test/smart-components/portfolio/__snapshots__/portfolios.test.js.snap @@ -4,6 +4,7 @@ exports[` should render correctly 1`] = ` ', () => { it('should mount and fetch data', (done) => { const store = mockStore(initialState); - apiClientMock.get(`${CATALOG_API_BASE}/portfolios`, mockOnce({ body: { data: [{ name: 'Foo', id: '11' }]}})); + apiClientMock.get(`${CATALOG_API_BASE}/portfolios?limit=50&offset=0`, mockOnce({ body: { data: [{ name: 'Foo', id: '11' }]}})); const expectedActions = [{ type: `${FETCH_PORTFOLIOS}_PENDING` }, expect.objectContaining({ @@ -78,7 +78,7 @@ describe('', () => { it('should mount filter portfolios', (done) => { const store = mockStore(initialState); - apiClientMock.get(`${CATALOG_API_BASE}/portfolios`, mockOnce({ body: { data: [{ name: 'Foo', id: '11' }]}})); + apiClientMock.get(`${CATALOG_API_BASE}/portfolios?limit=50&offset=0`, mockOnce({ body: { data: [{ name: 'Foo', id: '11' }]}})); const wrapper = mount( @@ -107,7 +107,7 @@ describe('', () => { } }); - apiClientMock.get(`${CATALOG_API_BASE}/portfolios`, mockOnce({ body: { data: [{ name: 'Foo', id: '11' }]}})); + apiClientMock.get(`${CATALOG_API_BASE}/portfolios?limit=50&offset=0`, mockOnce({ body: { data: [{ name: 'Foo', id: '11' }]}})); const wrapper = mount( diff --git a/src/toolbar/schemas/portfolios-toolbar.schema.js b/src/toolbar/schemas/portfolios-toolbar.schema.js index 49972ab72..ed23bc351 100644 --- a/src/toolbar/schemas/portfolios-toolbar.schema.js +++ b/src/toolbar/schemas/portfolios-toolbar.schema.js @@ -1,7 +1,11 @@ import { toolbarComponentTypes } from '../toolbar-mapper'; import { createSingleItemGroup, createLinkButton } from '../helpers'; +import AsyncPagination from '../../smart-components/common/async-pagination'; + const createPortfolioToolbarSchema = ({ + meta, + fetchPortfolios, filterProps: { searchValue, onFilterChange, @@ -16,29 +20,42 @@ const createPortfolioToolbarSchema = ({ key: 'portfolios-toolbar-title', title: 'Portfolios' }, { - component: toolbarComponentTypes.TOOLBAR, - key: 'main-portfolio-toolbar', - className: 'pf-u-mt-md', - fields: [ - createSingleItemGroup({ - groupName: 'filter-group', - component: toolbarComponentTypes.FILTER_TOOLBAR_ITEM, - key: 'filter-input', - searchValue, - onFilterChange, - placeholder - }), - createSingleItemGroup({ - groupName: 'portfolio-button-group', - key: 'create-portfolio', - ...createLinkButton({ - to: '/portfolios/add-portfolio', - variant: 'primary', - key: 'create-portfolio-button', - 'aria-label': 'Create portfolio', - title: 'Create portfolio' - }) - }) ] + component: toolbarComponentTypes.LEVEL, + key: 'porftolios-actions', + fields: [{ + component: toolbarComponentTypes.TOOLBAR, + key: 'main-portfolio-toolbar', + className: 'pf-u-mt-md', + fields: [ + createSingleItemGroup({ + groupName: 'filter-group', + component: toolbarComponentTypes.FILTER_TOOLBAR_ITEM, + key: 'filter-input', + searchValue, + onFilterChange, + placeholder + }), + createSingleItemGroup({ + groupName: 'portfolio-button-group', + key: 'create-portfolio', + ...createLinkButton({ + to: '/portfolios/add-portfolio', + variant: 'primary', + key: 'create-portfolio-button', + 'aria-label': 'Create portfolio', + title: 'Create portfolio' + }) + }) ] + }, { + component: toolbarComponentTypes.LEVEL_ITEM, + key: 'pagination-item', + fields: [{ + component: AsyncPagination, + key: 'porftolios-pagination', + meta, + apiRequest: fetchPortfolios + }] + }] }] }] }); From 376f4a04d5c5f232734e16203bf894db4695b140 Mon Sep 17 00:00:00 2001 From: Martin Marosi Date: Thu, 11 Jul 2019 13:35:16 +0200 Subject: [PATCH 3/4] Added pagination meta data for portfolio items. --- src/redux/actions/portfolio-actions.js | 4 ++-- src/redux/reducers/portfolio-reducer.js | 2 +- src/smart-components/order/order-item.js | 2 +- src/smart-components/order/orders.js | 2 +- src/smart-components/portfolio/portfolio.js | 2 +- .../redux/actions/portfolio-actions.test.js | 9 ++++---- .../portfolio/portfolio.test.js | 21 ++++++++++--------- .../portfolio/portfolios.test.js | 2 +- 8 files changed, 23 insertions(+), 21 deletions(-) diff --git a/src/redux/actions/portfolio-actions.js b/src/redux/actions/portfolio-actions.js index 2c16cb587..4643922ad 100644 --- a/src/redux/actions/portfolio-actions.js +++ b/src/redux/actions/portfolio-actions.js @@ -16,7 +16,7 @@ export const fetchPortfolios = (...args) => (dispatch) => { export const fetchPortfolioItems = apiProps => ({ type: ActionTypes.FETCH_PORTFOLIO_ITEMS, - payload: PortfolioHelper.getPortfolioItems(apiProps).then(({ data }) => data) + payload: PortfolioHelper.getPortfolioItems(apiProps) }); export const fetchPortfolioItem = (portfolioItemId) => ({ @@ -26,7 +26,7 @@ export const fetchPortfolioItem = (portfolioItemId) => ({ export const fetchPortfolioItemsWithPortfolio = apiProps => ({ type: ActionTypes.FETCH_PORTFOLIO_ITEMS_WITH_PORTFOLIO, - payload: PortfolioHelper.getPortfolioItemsWithPortfolio(apiProps).then(({ data }) => data) + payload: PortfolioHelper.getPortfolioItemsWithPortfolio(apiProps) }); export const fetchSelectedPortfolio = id => ({ diff --git a/src/redux/reducers/portfolio-reducer.js b/src/redux/reducers/portfolio-reducer.js index fbc509efc..2cb512fd9 100644 --- a/src/redux/reducers/portfolio-reducer.js +++ b/src/redux/reducers/portfolio-reducer.js @@ -13,7 +13,7 @@ import { // Initial State export const portfoliosInitialState = { - portfolioItems: [], + portfolioItems: { data: [], meta: {}}, portfolioItem: {}, portfolios: { data: [], meta: {}}, portfolio: {}, diff --git a/src/smart-components/order/order-item.js b/src/smart-components/order/order-item.js index 89009d4bc..85ee043c6 100644 --- a/src/smart-components/order/order-item.js +++ b/src/smart-components/order/order-item.js @@ -148,7 +148,7 @@ OrderItem.defaultProps = { const mapStateToProps = ({ orderReducer: { linkedOrders }, portfolioReducer: { portfolioItems }}, { index, type }) => ({ item: linkedOrders[type][index], - portfolioItems + portfolioItems: portfolioItems.data }); export default connect(mapStateToProps)(OrderItem); diff --git a/src/smart-components/order/orders.js b/src/smart-components/order/orders.js index 7519f12e7..bf4457ec8 100644 --- a/src/smart-components/order/orders.js +++ b/src/smart-components/order/orders.js @@ -70,7 +70,7 @@ const Orders = ({ getLinkedOrders, fetchPortfolioItems, isLoading, linkedOrders: const mapStateToProps = ({ orderReducer: { linkedOrders, isLoading }, portfolioReducer: { portfolioItems, isLoading: portfolioLoading }}) => ({ linkedOrders, isLoading: isLoading || portfolioLoading, - portfolioItems + portfolioItems: portfolioItems.data }); const mapDispatchToProps = dispatch => bindActionCreators({ diff --git a/src/smart-components/portfolio/portfolio.js b/src/smart-components/portfolio/portfolio.js index 1f70eb344..fa238c66f 100644 --- a/src/smart-components/portfolio/portfolio.js +++ b/src/smart-components/portfolio/portfolio.js @@ -132,7 +132,7 @@ const Portfolio = props => { const mapStateToProps = ({ portfolioReducer: { selectedPortfolio, portfolioItems }}) => ({ portfolio: selectedPortfolio, - portfolioItems + portfolioItems: portfolioItems.data }); const mapDispatchToProps = dispatch => bindActionCreators({ diff --git a/src/test/redux/actions/portfolio-actions.test.js b/src/test/redux/actions/portfolio-actions.test.js index f610c5b28..b8f250cbc 100644 --- a/src/test/redux/actions/portfolio-actions.test.js +++ b/src/test/redux/actions/portfolio-actions.test.js @@ -98,7 +98,7 @@ describe('Portfolio actions', () => { type: `${FETCH_PORTFOLIO_ITEMS}_PENDING` }, { type: `${FETCH_PORTFOLIO_ITEMS}_FULFILLED`, - payload: expect.any(Array) + payload: { data: [ 'foo' ]} }]; return store.dispatch(fetchPortfolioItems(123)) @@ -116,7 +116,7 @@ describe('Portfolio actions', () => { type: `${FETCH_PORTFOLIO_ITEMS_WITH_PORTFOLIO}_PENDING` }, { type: `${FETCH_PORTFOLIO_ITEMS_WITH_PORTFOLIO}_FULFILLED`, - payload: expect.any(Array) + payload: { data: [ 'foo' ]} }]; return store.dispatch(fetchPortfolioItemsWithPortfolio(123)) @@ -240,7 +240,8 @@ describe('Portfolio actions', () => { }, { type: `${FETCH_PORTFOLIO_ITEMS_WITH_PORTFOLIO}_PENDING` }, { - type: `${FETCH_PORTFOLIO_ITEMS_WITH_PORTFOLIO}_FULFILLED` + type: `${FETCH_PORTFOLIO_ITEMS_WITH_PORTFOLIO}_FULFILLED`, + payload: [] }, expect.objectContaining({ type: ADD_NOTIFICATION }), { type: `${REMOVE_PORTFOLIO_ITEMS}_FULFILLED` @@ -284,7 +285,7 @@ describe('Portfolio actions', () => { type: `${FETCH_PORTFOLIO_ITEMS_WITH_PORTFOLIO}_PENDING` }, { type: `${FETCH_PORTFOLIO_ITEMS_WITH_PORTFOLIO}_FULFILLED`, - payload: [] + payload: { data: []} }, expect.objectContaining({ type: ADD_NOTIFICATION }) ]; apiClientMock.post(CATALOG_API_BASE + '/portfolio_items/1/undelete', mockOnce({ body: { id: '1' }})); diff --git a/src/test/smart-components/portfolio/portfolio.test.js b/src/test/smart-components/portfolio/portfolio.test.js index c0f62147d..5c24fea43 100644 --- a/src/test/smart-components/portfolio/portfolio.test.js +++ b/src/test/smart-components/portfolio/portfolio.test.js @@ -47,6 +47,7 @@ describe('', () => { id: '123', name: 'Foo' }, + portfolioItems: { data: []}, portfolios: { data: [{ id: '123', name: 'bar', @@ -119,12 +120,12 @@ describe('', () => { id: '123', name: 'Foo' }, - portfolioItems: [{ + portfolioItems: { data: [{ id: '123', name: 'Foo', description: 'desc', modified: 'sometimes' - }] + }]} } }); @@ -159,12 +160,12 @@ describe('', () => { platformReducer: { platforms: []}, portfolioReducer: { ...initialState.portfolioReducer, - portfolioItems: [{ + portfolioItems: { data: [{ id: '123', name: 'Foo', description: 'desc', modified: 'sometimes' - }] + }]} } }); apiClientMock.get(`${CATALOG_API_BASE}/portfolios/123/portfolio_items`, mockOnce({ body: { data: []}})); @@ -192,12 +193,12 @@ describe('', () => { }, portfolioReducer: { ...initialState.portfolioReducer, - portfolioItems: [{ + portfolioItems: { data: [{ id: '123', name: 'Foo', description: 'desc', modified: 'sometimes' - }] + }]} } }); apiClientMock.get(`${CATALOG_API_BASE}/portfolios/123/portfolio_items`, mockOnce({ body: { data: []}})); @@ -222,12 +223,12 @@ describe('', () => { platformReducer: { platforms: []}, portfolioReducer: { ...initialState.portfolioReducer, - portfolioItems: [{ + portfolioItems: { data: [{ id: '123', name: 'Foo', description: 'desc', modified: 'sometimes' - }] + }]} } }); apiClientMock.get(`${CATALOG_API_BASE}/portfolios/123/portfolio_items`, mockOnce({ body: { data: []}})); @@ -261,12 +262,12 @@ describe('', () => { id: '321', name: 'Foo' }, - portfolioItems: [{ + portfolioItems: { data: [{ id: '321', name: 'Foo', description: 'desc', modified: 'sometimes' - }] + }]} } }); const restoreKey = 'restore-321'; diff --git a/src/test/smart-components/portfolio/portfolios.test.js b/src/test/smart-components/portfolio/portfolios.test.js index 1726136bb..a2b105c6c 100644 --- a/src/test/smart-components/portfolio/portfolios.test.js +++ b/src/test/smart-components/portfolio/portfolios.test.js @@ -103,7 +103,7 @@ describe('', () => { portfolioReducer: { ...initialState.portfolioReducer, isLoading: true, - portfolios: [] + portfolios: { data: []} } }); From e2aa9ce78e3c08b0141f034e0821e6803e9bd7f9 Mon Sep 17 00:00:00 2001 From: Martin Marosi Date: Thu, 11 Jul 2019 14:20:11 +0200 Subject: [PATCH 4/4] Added pagination to portfolio items screen. --- src/helpers/portfolio/portfolio-helper.js | 4 +- src/redux/actions/portfolio-actions.js | 8 +-- .../portfolio/portfolio-items.js | 18 ++++- src/smart-components/portfolio/portfolio.js | 27 ++++--- .../redux/actions/portfolio-actions.test.js | 4 +- .../portfolio/portfolio.test.js | 18 ++--- .../schemas/portfolio-toolbar.schema.js | 72 ++++++++++++------- .../schemas/portfolios-toolbar.schema.js | 2 +- 8 files changed, 96 insertions(+), 57 deletions(-) diff --git a/src/helpers/portfolio/portfolio-helper.js b/src/helpers/portfolio/portfolio-helper.js index 8764046d8..d296c0f5f 100644 --- a/src/helpers/portfolio/portfolio-helper.js +++ b/src/helpers/portfolio/portfolio-helper.js @@ -25,8 +25,8 @@ export function getPortfolio(portfolioId) { return portfolioApi.showPortfolio(portfolioId); } -export function getPortfolioItemsWithPortfolio(portfolioId) { - return axiosInstance.get(`${CATALOG_API_BASE}/portfolios/${portfolioId}/portfolio_items`); +export function getPortfolioItemsWithPortfolio(portfolioId, { limit, offset } = {}) { + return portfolioApi.fetchPortfolioItemsWithPortfolio(portfolioId, limit, offset); } // TO DO - change to use the API call that adds multiple items to a portfolio when available diff --git a/src/redux/actions/portfolio-actions.js b/src/redux/actions/portfolio-actions.js index 4643922ad..8b4be9dfc 100644 --- a/src/redux/actions/portfolio-actions.js +++ b/src/redux/actions/portfolio-actions.js @@ -24,9 +24,9 @@ export const fetchPortfolioItem = (portfolioItemId) => ({ payload: PortfolioHelper.getPortfolioItem(portfolioItemId) }); -export const fetchPortfolioItemsWithPortfolio = apiProps => ({ +export const fetchPortfolioItemsWithPortfolio = (...args) => ({ type: ActionTypes.FETCH_PORTFOLIO_ITEMS_WITH_PORTFOLIO, - payload: PortfolioHelper.getPortfolioItemsWithPortfolio(apiProps) + payload: PortfolioHelper.getPortfolioItemsWithPortfolio(...args) }); export const fetchSelectedPortfolio = id => ({ @@ -137,9 +137,9 @@ export const removeProductsFromPortfolio = (portfolioItems, portfolioName) => (d dispatch({ type: `${ActionTypes.REMOVE_PORTFOLIO_ITEMS}_PENDING` }); - const { portfolioReducer: { selectedPortfolio: { id: portfolioId }}} = getState(); + const { portfolioReducer: { portfolioItems: { meta }, selectedPortfolio: { id: portfolioId }}} = getState(); return PortfolioHelper.removePortfolioItems(portfolioItems) - .then(data => dispatch(fetchPortfolioItemsWithPortfolio(portfolioId)).then(() => data)) + .then(data => dispatch(fetchPortfolioItemsWithPortfolio(portfolioId, meta)).then(() => data)) .then(data => { return dispatch({ type: ADD_NOTIFICATION, diff --git a/src/smart-components/portfolio/portfolio-items.js b/src/smart-components/portfolio/portfolio-items.js index 856bdafcd..9992c25ae 100644 --- a/src/smart-components/portfolio/portfolio-items.js +++ b/src/smart-components/portfolio/portfolio-items.js @@ -25,7 +25,10 @@ const PortolioItems = ({ copyInProgress, removeProducts, copyPortfolio, - portfolioRoute + portfolioRoute, + pagination, + fetchPortfolioItemsWithPortfolio, + portfolio: { id } }) => ( removeProducts(selectedItems), - itemsSelected: selectedItems.length > 0 + itemsSelected: selectedItems.length > 0, + meta: pagination, + fetchPortfolioItemsWithPortfolio, + portfolioId: id }) } /> @@ -67,8 +73,14 @@ PortolioItems.propTypes = { isLoading: PropTypes.bool, copyInProgress: PropTypes.bool, removeProducts: PropTypes.func.isRequired, - copyPortfolio: PropTypes.func.isRequired + copyPortfolio: PropTypes.func.isRequired, + pagination: PropTypes.object.isRequired, + fetchPortfolioItemsWithPortfolio: PropTypes.func.isRequired, + portfolio: PropTypes.shape({ id: PropTypes.string }) +}; +PortolioItems.defaultProps = { + portfolio: {} }; export default PortolioItems; diff --git a/src/smart-components/portfolio/portfolio.js b/src/smart-components/portfolio/portfolio.js index fa238c66f..82d15c31b 100644 --- a/src/smart-components/portfolio/portfolio.js +++ b/src/smart-components/portfolio/portfolio.js @@ -8,6 +8,7 @@ import PortfolioItem from './portfolio-item'; import PortolioItems from './portfolio-items'; import { scrollToTop } from '../../helpers/shared/helpers'; import AddProductsToPortfolio from './add-products-to-portfolio'; +import { defaultSettings } from '../../helpers/shared/pagination'; import { filterServiceOffering } from '../../helpers/shared/helpers'; import { toggleArraySelection } from '../../helpers/shared/redux-mutators'; import PortfolioItemDetail from './portfolio-item-detail/portfolio-item-detail'; @@ -43,7 +44,7 @@ const Portfolio = props => { dispatch({ type: 'setIsFetching', payload: true }); Promise.all([ props.fetchSelectedPortfolio(apiProps), - props.fetchPortfolioItemsWithPortfolio(apiProps) + props.fetchPortfolioItemsWithPortfolio(apiProps, defaultSettings) ]) .then(() => dispatch({ type: 'setIsFetching', payload: false })) .catch(() => dispatch({ type: 'setIsFetching', payload: false })); @@ -99,7 +100,7 @@ const Portfolio = props => { removeInProgress={ removeInProgress } /> )), - isLoading: isFetching + isLoading: isFetching || props.isLoading }; return ( @@ -110,10 +111,10 @@ const Portfolio = props => { ( + render={ args => ( { copyInProgress={ copyInProgress } removeProducts={ removeProducts } copyPortfolio={ copyPortfolio } + fetchPortfolioItemsWithPortfolio={ props.fetchPortfolioItemsWithPortfolio } + portfolio={ props.portfolio } + pagination={ props.pagination } /> ) } /> @@ -130,9 +134,11 @@ const Portfolio = props => { ); }; -const mapStateToProps = ({ portfolioReducer: { selectedPortfolio, portfolioItems }}) => ({ +const mapStateToProps = ({ portfolioReducer: { selectedPortfolio, portfolioItems, isLoading }}) => ({ portfolio: selectedPortfolio, - portfolioItems: portfolioItems.data + portfolioItems: portfolioItems.data, + pagination: portfolioItems.meta, + isLoading }); const mapDispatchToProps = dispatch => bindActionCreators({ @@ -150,17 +156,20 @@ Portfolio.propTypes = { fetchPortfolios: PropTypes.func.isRequired, portfolio: PropTypes.shape({ name: PropTypes.string, - id: PropTypes.string.isRequired + id: PropTypes.string }), location: PropTypes.object, history: PropTypes.object, portfolioItems: PropTypes.array, removeProductsFromPortfolio: PropTypes.func.isRequired, - copyPortfolio: PropTypes.func.isRequired + copyPortfolio: PropTypes.func.isRequired, + pagination: PropTypes.object, + isLoading: PropTypes.bool }; Portfolio.defaultProps = { - portfolioItems: [] + portfolioItems: [], + portfolio: {} }; export default withRouter(connect(mapStateToProps, mapDispatchToProps)(Portfolio)); diff --git a/src/test/redux/actions/portfolio-actions.test.js b/src/test/redux/actions/portfolio-actions.test.js index b8f250cbc..b7809c409 100644 --- a/src/test/redux/actions/portfolio-actions.test.js +++ b/src/test/redux/actions/portfolio-actions.test.js @@ -234,7 +234,7 @@ describe('Portfolio actions', () => { }); it('should create correct actions after remove portfolio items action success', () => { - const store = mockStore({ portfolioReducer: { selectedPortfolio: { id: '123' }}}); + const store = mockStore({ portfolioReducer: { portfolioItems: { meta: {}}, selectedPortfolio: { id: '123' }}}); const expectedActions = [{ type: `${REMOVE_PORTFOLIO_ITEMS}_PENDING` }, { @@ -257,7 +257,7 @@ describe('Portfolio actions', () => { }); it('should create correct actions after remove portfolio items action fals', () => { - const store = mockStore({ portfolioReducer: { selectedPortfolio: { id: '123' }}}); + const store = mockStore({ portfolioReducer: { portfolioItems: { meta: {}}, selectedPortfolio: { id: '123' }}}); const expectedActions = [{ type: `${REMOVE_PORTFOLIO_ITEMS}_PENDING` }, diff --git a/src/test/smart-components/portfolio/portfolio.test.js b/src/test/smart-components/portfolio/portfolio.test.js index 5c24fea43..02425451f 100644 --- a/src/test/smart-components/portfolio/portfolio.test.js +++ b/src/test/smart-components/portfolio/portfolio.test.js @@ -78,8 +78,8 @@ describe('', () => { apiClientMock.get(`${CATALOG_API_BASE}/portfolios/123`, mockOnce({ body: {}})); - apiClientMock.get(`${CATALOG_API_BASE}/portfolios/123/portfolio_items`, mockOnce({ body: { data: []}})); - apiClientMock.get(`${CATALOG_API_BASE}/portfolios/123/portfolio_items`, mockOnce({ body: { data: []}})); + apiClientMock.get(`${CATALOG_API_BASE}/portfolios/123/portfolio_items?limit=50&offset=0`, mockOnce({ body: { data: []}})); + apiClientMock.get(`${CATALOG_API_BASE}/portfolios/123/portfolio_items?limit=50&offset=0`, mockOnce({ body: { data: []}})); mount( @@ -94,7 +94,7 @@ describe('', () => { it('should mount and render add products page', (done) => { const store = mockStore({ ...initialState, platformReducer: { platforms: [], platformItems: {}}}); - apiClientMock.get(`${CATALOG_API_BASE}/portfolios/123/portfolio_items`, mockOnce({ body: { data: []}})); + apiClientMock.get(`${CATALOG_API_BASE}/portfolios/123/portfolio_items?limit=50&offset=0`, mockOnce({ body: { data: []}})); apiClientMock.get(`${CATALOG_API_BASE}/portfolios/123`, mockOnce({ body: { data: []}})); apiClientMock.get(`${SOURCES_API_BASE}/sources`, mockOnce({ body: { data: []}})); @@ -131,12 +131,12 @@ describe('', () => { apiClientMock.get(`${CATALOG_API_BASE}/portfolios/123`, mockOnce({ body: { data: []}})); apiClientMock.get(`${CATALOG_API_BASE}/portfolios/123`, mockOnce({ body: { data: []}})); - apiClientMock.delete(`${CATALOG_API_BASE}/portfolio_items/123`, mockOnce((req, res) => { + apiClientMock.delete(`${CATALOG_API_BASE}/portfolio_items/123?limit=50&offset=0`, mockOnce((req, res) => { expect(req).toBeTruthy(); return res.status(200); })); apiClientMock.get(`${SOURCES_API_BASE}/sources`, mockOnce({ body: { data: []}})); - apiClientMock.get(`${CATALOG_API_BASE}/portfolios/123/portfolio_items`, mockOnce({ body: { data: []}})); + apiClientMock.get(`${CATALOG_API_BASE}/portfolios/123/portfolio_items?limit=50&offset=0`, mockOnce({ body: { data: []}})); const wrapper = mount( @@ -168,7 +168,7 @@ describe('', () => { }]} } }); - apiClientMock.get(`${CATALOG_API_BASE}/portfolios/123/portfolio_items`, mockOnce({ body: { data: []}})); + apiClientMock.get(`${CATALOG_API_BASE}/portfolios/123/portfolio_items?limit=50&offset=0`, mockOnce({ body: { data: []}})); apiClientMock.get(`${CATALOG_API_BASE}/portfolios/123`, mockOnce({ body: { data: []}})); apiClientMock.get(`${SOURCES_API_BASE}/sources`, mockOnce({ body: { data: []}})); @@ -201,7 +201,7 @@ describe('', () => { }]} } }); - apiClientMock.get(`${CATALOG_API_BASE}/portfolios/123/portfolio_items`, mockOnce({ body: { data: []}})); + apiClientMock.get(`${CATALOG_API_BASE}/portfolios/123/portfolio_items?limit=50&offset=0`, mockOnce({ body: { data: []}})); apiClientMock.get(`${CATALOG_API_BASE}/portfolios/123`, mockOnce({ body: { data: []}})); apiClientMock.get(`${SOURCES_API_BASE}/sources`, mockOnce({ body: { data: []}})); @@ -231,7 +231,7 @@ describe('', () => { }]} } }); - apiClientMock.get(`${CATALOG_API_BASE}/portfolios/123/portfolio_items`, mockOnce({ body: { data: []}})); + apiClientMock.get(`${CATALOG_API_BASE}/portfolios/123/portfolio_items?limit=50&offset=0`, mockOnce({ body: { data: []}})); apiClientMock.get(`${CATALOG_API_BASE}/portfolios/123`, mockOnce({ body: { data: []}})); apiClientMock.get(`${SOURCES_API_BASE}/sources`, mockOnce({ body: { data: []}})); @@ -272,7 +272,7 @@ describe('', () => { }); const restoreKey = 'restore-321'; - apiClientMock.get(`${CATALOG_API_BASE}/portfolios/321/portfolio_items`, mockOnce({ body: { data: []}})); + apiClientMock.get(`${CATALOG_API_BASE}/portfolios/321/portfolio_items?limit=50&offset=0`, mockOnce({ body: { data: []}})); apiClientMock.get(`${CATALOG_API_BASE}/portfolios/321`, mockOnce({ body: { data: []}})); apiClientMock.get(`${SOURCES_API_BASE}/sources`, mockOnce({ body: { data: []}})); diff --git a/src/toolbar/schemas/portfolio-toolbar.schema.js b/src/toolbar/schemas/portfolio-toolbar.schema.js index 41d022974..ed7ca23d0 100644 --- a/src/toolbar/schemas/portfolio-toolbar.schema.js +++ b/src/toolbar/schemas/portfolio-toolbar.schema.js @@ -5,6 +5,7 @@ import { Dropdown, DropdownPosition, KebabToggle, DropdownItem } from '@patternf import { toolbarComponentTypes } from '../toolbar-mapper'; import { createSingleItemGroup, createLinkButton } from '../helpers'; +import AsyncPagination from '../../smart-components/common/async-pagination'; /** * Cannot be anonymous function. Requires Component.diplayName to work with PF4 refs @@ -81,6 +82,9 @@ const createPortfolioToolbarSchema = ({ isLoading, removeProducts, itemsSelected, + meta, + fetchPortfolioItemsWithPortfolio, + portfolioId, filterProps: { searchValue, onFilterChange, @@ -121,35 +125,49 @@ const createPortfolioToolbarSchema = ({ }] }] }, { - component: toolbarComponentTypes.TOOLBAR, - key: 'portfolio-items-actions', - fields: [ - createSingleItemGroup({ - groupName: 'filter-portfolio-items', - component: toolbarComponentTypes.FILTER_TOOLBAR_ITEM, - key: 'portfolio-items-filter', - searchValue, - onFilterChange, - placeholder + component: toolbarComponentTypes.LEVEL, + key: 'porftolio-items-actions', + fields: [{ + component: toolbarComponentTypes.TOOLBAR, + key: 'portfolio-items-actions', + fields: [ + createSingleItemGroup({ + groupName: 'filter-portfolio-items', + component: toolbarComponentTypes.FILTER_TOOLBAR_ITEM, + key: 'portfolio-items-filter', + searchValue, + onFilterChange, + placeholder - }), - createSingleItemGroup({ - groupName: 'add-portfolio-items', - key: 'portfolio-items-add-group', - ...createLinkButton({ - to: addProductsRoute, - isDisabled: isLoading || copyInProgress, - variant: 'primary', - title: 'Add products', - key: 'add-products-button' - }) - }), { - component: PortfolioItemsActionsDropdown, - isDisabled: copyInProgress, - key: 'remove-products-actions-dropdown', - removeProducts, - itemsSelected + }), + createSingleItemGroup({ + groupName: 'add-portfolio-items', + key: 'portfolio-items-add-group', + ...createLinkButton({ + to: addProductsRoute, + isDisabled: isLoading || copyInProgress, + variant: 'primary', + title: 'Add products', + key: 'add-products-button' + }) + }), { + component: PortfolioItemsActionsDropdown, + isDisabled: copyInProgress, + key: 'remove-products-actions-dropdown', + removeProducts, + itemsSelected + }] + }, { + component: toolbarComponentTypes.LEVEL_ITEM, + key: 'pagination-item', + fields: [{ + component: AsyncPagination, + key: 'porftolio-items-pagination', + meta, + apiRequest: fetchPortfolioItemsWithPortfolio, + apiProps: portfolioId }] + }] }] }] }); diff --git a/src/toolbar/schemas/portfolios-toolbar.schema.js b/src/toolbar/schemas/portfolios-toolbar.schema.js index ed23bc351..8dc3cd71b 100644 --- a/src/toolbar/schemas/portfolios-toolbar.schema.js +++ b/src/toolbar/schemas/portfolios-toolbar.schema.js @@ -46,7 +46,7 @@ const createPortfolioToolbarSchema = ({ title: 'Create portfolio' }) }) ] - }, { + }, { component: toolbarComponentTypes.LEVEL_ITEM, key: 'pagination-item', fields: [{