Browse Source

SONAR-15488 Fix pagination if total is undefined and page size is known

Co-authored-by: Guillaume Peoch <guillaume.peoch@sonarsource.com>
tags/8.9.3.48735
Wouter Admiraal 2 years ago
parent
commit
37b22540ca

+ 2
- 1
server/sonar-web/src/main/js/apps/create/project/GitlabProjectSelectionForm.tsx View File

@@ -21,7 +21,6 @@ import * as React from 'react';
import { FormattedMessage } from 'react-intl';
import { Link } from 'react-router';
import { Button } from 'sonar-ui-common/components/controls/buttons';
import ListFooter from 'sonar-ui-common/components/controls/ListFooter';
import SearchBox from 'sonar-ui-common/components/controls/SearchBox';
import Tooltip from 'sonar-ui-common/components/controls/Tooltip';
import CheckIcon from 'sonar-ui-common/components/icons/CheckIcon';
@@ -30,6 +29,7 @@ import QualifierIcon from 'sonar-ui-common/components/icons/QualifierIcon';
import { Alert } from 'sonar-ui-common/components/ui/Alert';
import DeferredSpinner from 'sonar-ui-common/components/ui/DeferredSpinner';
import { translate } from 'sonar-ui-common/helpers/l10n';
import ListFooter from '../../../components/controls/ListFooter';
import { getProjectUrl } from '../../../helpers/urls';
import { GitlabProject } from '../../../types/alm-integration';
import { ComponentQualifier } from '../../../types/component';
@@ -159,6 +159,7 @@ export default function GitlabProjectSelectionForm(props: GitlabProjectSelection
count={projects.length}
loadMore={props.onLoadMore}
loading={loadingMore}
pageSize={projectsPaging.pageSize}
total={projectsPaging.total}
/>
</div>

+ 1
- 1
server/sonar-web/src/main/js/apps/create/project/__tests__/GitlabProjectSelectionForm-test.tsx View File

@@ -20,8 +20,8 @@
import { shallow } from 'enzyme';
import * as React from 'react';
import { Button } from 'sonar-ui-common/components/controls/buttons';
import ListFooter from 'sonar-ui-common/components/controls/ListFooter';
import SearchBox from 'sonar-ui-common/components/controls/SearchBox';
import ListFooter from '../../../../components/controls/ListFooter';
import { mockGitlabProject } from '../../../../helpers/mocks/alm-integrations';
import GitlabProjectSelectionForm, {
GitlabProjectSelectionFormProps

+ 3
- 0
server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/GitlabProjectSelectionForm-test.tsx.snap View File

@@ -137,6 +137,7 @@ exports[`should render correctly: importing 1`] = `
count={2}
loadMore={[MockFunction]}
loading={false}
pageSize={30}
total={2}
/>
</div>
@@ -194,6 +195,7 @@ exports[`should render correctly: no projects when searching 1`] = `
count={0}
loadMore={[MockFunction]}
loading={false}
pageSize={30}
total={0}
/>
</div>
@@ -336,6 +338,7 @@ exports[`should render correctly: projects 1`] = `
count={2}
loadMore={[MockFunction]}
loading={false}
pageSize={30}
total={2}
/>
</div>

+ 82
- 0
server/sonar-web/src/main/js/components/controls/ListFooter.tsx View File

@@ -0,0 +1,82 @@
/*
* SonarQube
* Copyright (C) 2009-2021 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import classNames from 'classnames';
import * as React from 'react';
import { Button } from 'sonar-ui-common/components/controls/buttons';
import DeferredSpinner from 'sonar-ui-common/components/ui/DeferredSpinner';
import { translate, translateWithParameters } from 'sonar-ui-common/helpers/l10n';
import { formatMeasure } from 'sonar-ui-common/helpers/measures';

export interface ListFooterProps {
count: number;
className?: string;
loading?: boolean;
loadMore?: () => void;
needReload?: boolean;
pageSize?: number;
reload?: () => void;
ready?: boolean;
total?: number;
}

export default function ListFooter(props: ListFooterProps) {
const { className, count, loading, needReload, total, pageSize, ready = true } = props;

let hasMore = false;
if (total !== undefined) {
hasMore = total > count;
} else if (pageSize !== undefined) {
hasMore = count % pageSize === 0;
}

let button;
if (needReload && props.reload) {
button = (
<Button className="spacer-left" data-test="reload" disabled={loading} onClick={props.reload}>
{translate('reload')}
</Button>
);
} else if (hasMore && props.loadMore) {
button = (
<Button
className="spacer-left"
disabled={loading}
data-test="show-more"
onClick={props.loadMore}>
{translate('show_more')}
</Button>
);
}

return (
<footer
className={classNames('spacer-top note text-center', { 'new-loading': !ready }, className)}>
{total !== undefined
? translateWithParameters(
'x_of_y_shown',
formatMeasure(count, 'INT', null),
formatMeasure(total, 'INT', null)
)
: translateWithParameters('x_show', formatMeasure(count, 'INT', null))}
{button}
{loading && <DeferredSpinner className="text-bottom spacer-left position-absolute" />}
</footer>
);
}

+ 74
- 0
server/sonar-web/src/main/js/components/controls/__tests__/ListFooter-test.tsx View File

@@ -0,0 +1,74 @@
/*
* SonarQube
* Copyright (C) 2009-2021 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import { shallow } from 'enzyme';
import * as React from 'react';
import { Button } from 'sonar-ui-common/components/controls/buttons';
import { click } from 'sonar-ui-common/helpers/testUtils';
import ListFooter, { ListFooterProps } from '../ListFooter';

it('should render correctly', () => {
expect(shallowRender()).toMatchSnapshot('default');
expect(shallowRender({ loading: true })).toMatchSnapshot('loading');
expect(shallowRender({ needReload: true, reload: jest.fn() })).toMatchSnapshot('reload');
expect(shallowRender({ loading: true, needReload: true, reload: jest.fn() })).toMatchSnapshot(
'reload, loading'
);
expect(shallowRender({ loadMore: undefined })).toMatchSnapshot(
'empty if no loadMore nor reload props'
);
expect(shallowRender({ count: 5 })).toMatchSnapshot('empty if everything is loaded');
expect(shallowRender({ total: undefined })).toMatchSnapshot('total undefined');
expect(shallowRender({ total: undefined, count: 60, pageSize: 30 })).toMatchSnapshot(
'force show load more button if count % pageSize is 0, and total is undefined'
);
});

it.each([
[undefined, 60, 30, true],
[undefined, 45, 30, false],
[undefined, 60, undefined, false],
[60, 60, 30, false]
])(
'handle showing load more button based on total, count and pageSize',
(total, count, pageSize, expected) => {
const wrapper = shallowRender({ total, count, pageSize });
expect(wrapper.find(Button).exists()).toBe(expected);
}
);

it('should properly call loadMore', () => {
const loadMore = jest.fn();
const wrapper = shallowRender({ loadMore });
click(wrapper.find(Button));
expect(loadMore).toBeCalled();
});

it('should properly call reload', () => {
const reload = jest.fn();
const wrapper = shallowRender({ needReload: true, reload });
click(wrapper.find(Button));
expect(reload).toBeCalled();
});

function shallowRender(props: Partial<ListFooterProps> = {}) {
return shallow<ListFooterProps>(
<ListFooter count={3} loadMore={jest.fn()} total={5} {...props} />
);
}

+ 108
- 0
server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/ListFooter-test.tsx.snap View File

@@ -0,0 +1,108 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should render correctly: default 1`] = `
<footer
className="spacer-top note text-center"
>
x_of_y_shown.3.5
<Button
className="spacer-left"
data-test="show-more"
onClick={[MockFunction]}
>
show_more
</Button>
</footer>
`;

exports[`should render correctly: empty if everything is loaded 1`] = `
<footer
className="spacer-top note text-center"
>
x_of_y_shown.5.5
</footer>
`;

exports[`should render correctly: empty if no loadMore nor reload props 1`] = `
<footer
className="spacer-top note text-center"
>
x_of_y_shown.3.5
</footer>
`;

exports[`should render correctly: force show load more button if count % pageSize is 0, and total is undefined 1`] = `
<footer
className="spacer-top note text-center"
>
x_show.60
<Button
className="spacer-left"
data-test="show-more"
onClick={[MockFunction]}
>
show_more
</Button>
</footer>
`;

exports[`should render correctly: loading 1`] = `
<footer
className="spacer-top note text-center"
>
x_of_y_shown.3.5
<Button
className="spacer-left"
data-test="show-more"
disabled={true}
onClick={[MockFunction]}
>
show_more
</Button>
<DeferredSpinner
className="text-bottom spacer-left position-absolute"
/>
</footer>
`;

exports[`should render correctly: reload 1`] = `
<footer
className="spacer-top note text-center"
>
x_of_y_shown.3.5
<Button
className="spacer-left"
data-test="reload"
onClick={[MockFunction]}
>
reload
</Button>
</footer>
`;

exports[`should render correctly: reload, loading 1`] = `
<footer
className="spacer-top note text-center"
>
x_of_y_shown.3.5
<Button
className="spacer-left"
data-test="reload"
disabled={true}
onClick={[MockFunction]}
>
reload
</Button>
<DeferredSpinner
className="text-bottom spacer-left position-absolute"
/>
</footer>
`;

exports[`should render correctly: total undefined 1`] = `
<footer
className="spacer-top note text-center"
>
x_show.3
</footer>
`;

Loading…
Cancel
Save