diff options
author | Stas Vilchik <vilchiks@gmail.com> | 2016-04-07 15:44:07 +0200 |
---|---|---|
committer | Stas Vilchik <vilchiks@gmail.com> | 2016-04-11 15:48:35 +0200 |
commit | 9bf2ebf04d26e49c844b264f767a0345cce0be20 (patch) | |
tree | 0c1402deb0243578e79718bf92de289f5b8efc4d /server/sonar-web | |
parent | c8b01d4f1520a14be831547f6b35c072a8b9517e (diff) | |
download | sonarqube-9bf2ebf04d26e49c844b264f767a0345cce0be20.tar.gz sonarqube-9bf2ebf04d26e49c844b264f767a0345cce0be20.zip |
SONAR-7531 Add pagination on the Code page
Diffstat (limited to 'server/sonar-web')
6 files changed, 93 insertions, 21 deletions
diff --git a/server/sonar-web/src/main/js/api/components.js b/server/sonar-web/src/main/js/api/components.js index ac53aaf2ab3..934d7a9c1bb 100644 --- a/server/sonar-web/src/main/js/api/components.js +++ b/server/sonar-web/src/main/js/api/components.js @@ -55,7 +55,7 @@ export function getComponentTree (strategy, componentKey, metrics = [], addition } export function getChildren (componentKey, metrics, additional) { - return getComponentTree('children', componentKey, metrics, additional).then(r => r.components); + return getComponentTree('children', componentKey, metrics, additional); } export function getComponentLeaves (componentKey, metrics, additional) { diff --git a/server/sonar-web/src/main/js/apps/code/actions/index.js b/server/sonar-web/src/main/js/apps/code/actions/index.js index fc143fa1ec1..2422d993362 100644 --- a/server/sonar-web/src/main/js/apps/code/actions/index.js +++ b/server/sonar-web/src/main/js/apps/code/actions/index.js @@ -40,8 +40,11 @@ const METRICS_WITH_COVERAGE = [ 'overall_coverage' ]; +const PAGE_SIZE = 100; + export const INIT = 'INIT'; export const BROWSE = 'BROWSE'; +export const LOAD_MORE = 'LOAD_MORE'; export const SEARCH = 'SEARCH'; export const SELECT_NEXT = 'SELECT_NEXT'; export const SELECT_PREV = 'SELECT_PREV'; @@ -58,12 +61,21 @@ export function initComponentAction (component, breadcrumbs = []) { }; } -export function browseAction (component, children = [], breadcrumbs = []) { +export function browseAction (component, children = [], breadcrumbs = [], total = 0) { return { type: BROWSE, component, children, - breadcrumbs + breadcrumbs, + total + }; +} + +export function loadMoreAction (children, page) { + return { + type: LOAD_MORE, + children, + page }; } @@ -108,17 +120,23 @@ function getPath (componentKey) { return '/' + encodeURIComponent(componentKey); } -function expandRootDir (components) { - const rootDir = components.find(component => component.qualifier === 'DIR' && component.name === '/'); +function expandRootDir ({ children, total, ...other }) { + const rootDir = children.find(component => component.qualifier === 'DIR' && component.name === '/'); if (rootDir) { - return getChildren(rootDir.key, METRICS_WITH_COVERAGE).then(files => { - return _.without([...components, ...files], rootDir); + return getChildren(rootDir.key, METRICS_WITH_COVERAGE).then(r => { + const nextChildren = _.without([...children, ...r.components], rootDir); + const nextTotal = total + r.components.length - /* root dir */ 1; + return { children: nextChildren, total: nextTotal, ...other }; }); } else { - return components; + return { children, total, ...other }; } } +function prepareChildren (r) { + return { children: r.components, total: r.paging.total, page: r.paging.pageIndex }; +} + function skipRootDir (breadcrumbs) { return breadcrumbs.filter(component => { return !(component.qualifier === 'DIR' && component.name === '/'); @@ -133,8 +151,8 @@ function retrieveComponentBase (componentKey, candidate) { function retrieveComponentChildren (componentKey, candidate) { return candidate && candidate.children ? - Promise.resolve(candidate.children) : - getChildren(componentKey, METRICS_WITH_COVERAGE).then(expandRootDir); + Promise.resolve({ children: candidate.children, total: candidate.total }) : + getChildren(componentKey, METRICS_WITH_COVERAGE, { ps: PAGE_SIZE }).then(prepareChildren).then(expandRootDir); } function retrieveComponentBreadcrumbs (componentKey, candidate) { @@ -195,7 +213,7 @@ export function browse (componentKey) { window.location = getComponentUrl(component.refKey); return new Promise(); } else { - dispatch(browseAction(component, children, breadcrumbs)); + dispatch(browseAction(component, children.children, breadcrumbs, children.total)); } }) .then(() => dispatch(pushPath(getPath(componentKey)))) @@ -207,6 +225,22 @@ export function browse (componentKey) { }; } +export function loadMore () { + return (dispatch, getState) => { + const { baseComponent, page } = getState().current; + return getChildren(baseComponent.key, METRICS_WITH_COVERAGE, { p: page + 1, ps: PAGE_SIZE }) + .then(prepareChildren) + .then(({ children }) => { + dispatch(loadMoreAction(children, page + 1)); + dispatch(stopFetching()); + }) + .catch(e => { + getErrorMessage(e.response) + .then(message => dispatch(raiseError(message))); + }); + }; +} + let debouncedSearch = function (query, baseComponent, dispatch) { if (query) { requestTree(query, baseComponent, dispatch); diff --git a/server/sonar-web/src/main/js/apps/code/components/Code.js b/server/sonar-web/src/main/js/apps/code/components/Code.js index 47e2e13cf2e..b8f63763f72 100644 --- a/server/sonar-web/src/main/js/apps/code/components/Code.js +++ b/server/sonar-web/src/main/js/apps/code/components/Code.js @@ -25,7 +25,8 @@ import Components from './Components'; import Breadcrumbs from './Breadcrumbs'; import SourceViewer from './SourceViewer'; import Search from './Search'; -import { initComponent, browse } from '../actions'; +import ListFooter from '../../../components/shared/list-footer'; +import { initComponent, browse, loadMore } from '../actions'; class Code extends Component { componentDidMount () { @@ -50,6 +51,11 @@ class Code extends Component { dispatch(browse(component.key)); } + handleLoadMore () { + const { dispatch } = this.props; + dispatch(loadMore()); + } + render () { const { fetching, @@ -59,7 +65,9 @@ class Code extends Component { sourceViewer, coverageMetric, searchResults, - errorMessage } = this.props; + errorMessage, + total + } = this.props; const shouldShowSearchResults = !!searchResults; const shouldShowSourceViewer = !!sourceViewer; const shouldShowComponents = !shouldShowSearchResults && !shouldShowSourceViewer && components; @@ -109,6 +117,13 @@ class Code extends Component { </div> )} + {shouldShowComponents && ( + <ListFooter + count={components.length} + total={total} + loadMore={this.handleLoadMore.bind(this)}/> + )} + {shouldShowSourceViewer && ( <div className="spacer-top"> <SourceViewer component={sourceViewer}/> @@ -129,6 +144,7 @@ export default connect(state => { sourceViewer: state.current.sourceViewer, coverageMetric: state.current.coverageMetric, searchResults: state.current.searchResults, - errorMessage: state.current.errorMessage + errorMessage: state.current.errorMessage, + total: state.current.total }; })(Code); diff --git a/server/sonar-web/src/main/js/apps/code/reducers/index.js b/server/sonar-web/src/main/js/apps/code/reducers/index.js index b76be02a510..f061926e0e4 100644 --- a/server/sonar-web/src/main/js/apps/code/reducers/index.js +++ b/server/sonar-web/src/main/js/apps/code/reducers/index.js @@ -19,8 +19,18 @@ */ import _ from 'underscore'; -import { INIT, BROWSE, SEARCH, UPDATE_QUERY, SELECT_NEXT, SELECT_PREV, START_FETCHING, STOP_FETCHING, - RAISE_ERROR } from '../actions'; +import { + INIT, + BROWSE, + LOAD_MORE, + SEARCH, + UPDATE_QUERY, + SELECT_NEXT, + SELECT_PREV, + START_FETCHING, + STOP_FETCHING, + RAISE_ERROR +} from '../actions'; function hasSourceCode (component) { return component.qualifier === 'FIL' || component.qualifier === 'UTS'; @@ -125,11 +135,19 @@ export function current (state = initialState, action) { components, breadcrumbs, sourceViewer, + total: action.total, + page: 1, searchResults: null, searchQuery: '', searchSelectedItem: null, errorMessage: null }; + case LOAD_MORE: + return { + ...state, + components: sortChildren([...state.components, ...action.children]), + page: action.page + }; case SEARCH: return { ...state, @@ -172,6 +190,7 @@ export function bucket (state = [], action) { case BROWSE: const candidate = Object.assign({}, action.component, { children: action.children, + total: action.total, breadcrumbs: action.breadcrumbs }); const nextState = merge(state, candidate); diff --git a/server/sonar-web/src/main/js/apps/component-measures/details/treemap/MeasureTreemap.js b/server/sonar-web/src/main/js/apps/component-measures/details/treemap/MeasureTreemap.js index 92615167cc3..7b75d0b65f5 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/details/treemap/MeasureTreemap.js +++ b/server/sonar-web/src/main/js/apps/component-measures/details/treemap/MeasureTreemap.js @@ -64,7 +64,7 @@ export default class MeasureTreemap extends React.Component { }; return getChildren(componentKey, metrics, options).then(r => { - const components = r.map(component => { + const components = r.components.map(component => { const measures = {}; const key = component.refKey || component.key; diff --git a/server/sonar-web/tests/apps/code/store-test.js b/server/sonar-web/tests/apps/code/store-test.js index de5ddbfadf1..155c84a8720 100644 --- a/server/sonar-web/tests/apps/code/store-test.js +++ b/server/sonar-web/tests/apps/code/store-test.js @@ -313,7 +313,7 @@ describe('Code :: Store', () => { const breadcrumbsBefore = [{ key: 'A' }]; const bucketAfter = [ - { key: 'A', breadcrumbs: [{ key: 'A' }], children: [{ key: 'B' }] }, + { key: 'A', breadcrumbs: [{ key: 'A' }], children: [{ key: 'B' }], total: 0 }, { key: 'B', breadcrumbs: [{ key: 'A' }, { key: 'B' }] } ]; @@ -335,7 +335,8 @@ describe('Code :: Store', () => { { key: 'A', breadcrumbs: [{ key: 'A' }], - children: [{ key: 'B' }] + children: [{ key: 'B' }], + total: 0 }, { key: 'B', @@ -360,12 +361,14 @@ describe('Code :: Store', () => { { key: 'A', breadcrumbs: [{ key: 'A' }], - children: [{ key: 'B' }] + children: [{ key: 'B' }], + total: 0 }, { key: 'B', breadcrumbs: [{ key: 'A' }, { key: 'B' }], - children: [{ key: 'C' }] + children: [{ key: 'C' }], + total: 0 }, { key: 'C', |