* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import styled from '@emotion/styled';
import classNames from 'classnames';
+import { ButtonSecondary, themeColor, withTheme } from 'design-system';
import * as React from 'react';
import { translate, translateWithParameters } from '../../helpers/l10n';
import { formatMeasure } from '../../helpers/measures';
+import { MetricType } from '../../types/metrics';
import DeferredSpinner from '../ui/DeferredSpinner';
import { Button } from './buttons';
reload?: () => void;
ready?: boolean;
total?: number;
+ useMIUIButtons?: boolean;
}
export default function ListFooter(props: ListFooterProps) {
total,
pageSize,
ready = true,
+ useMIUIButtons = false,
} = props;
const rootNode = React.useRef<HTMLDivElement>(null);
let button;
if (needReload && props.reload) {
- button = (
- <Button className="spacer-left" data-test="reload" disabled={loading} onClick={props.reload}>
- {translate('reload')}
- </Button>
+ button = React.createElement(
+ useMIUIButtons ? ButtonSecondary : Button,
+ {
+ 'data-test': 'reload',
+ className: classNames('sw-ml-2', { 'sw-body-sm': useMIUIButtons }),
+ disabled: loading,
+ onClick: props.reload,
+ } as Button['props'],
+ translate('reload')
);
} else if (hasMore && props.loadMore) {
- button = (
- <Button
- aria-label={loadMoreAriaLabel}
- className="spacer-left"
- disabled={loading}
- data-test="show-more"
- onClick={onLoadMore}
- >
- {translate('show_more')}
- </Button>
+ button = React.createElement(
+ useMIUIButtons ? ButtonSecondary : Button,
+ {
+ 'aria-label': loadMoreAriaLabel,
+ 'data-test': 'show-more',
+ className: classNames('sw-ml-2', { 'sw-body-sm': useMIUIButtons }),
+ disabled: loading,
+ onClick: onLoadMore,
+ } as Button['props'],
+ translate('show_more')
);
}
return (
- <div
+ <StyledDiv
tabIndex={-1}
ref={rootNode}
className={classNames(
- 'list-footer spacer-top note text-center',
- { 'new-loading': !ready },
+ 'list-footer', // .list-footer is only used by Selenium tests; we should find a way to remove it.
+ 'sw-body-sm sw-mt-4 sw-flex sw-items-center sw-justify-center',
+ { 'sw-opacity-50 sw-duration-500 sw-ease-in-out': !ready },
className
)}
>
{total !== undefined
? translateWithParameters(
'x_of_y_shown',
- formatMeasure(count, 'INT', null),
- formatMeasure(total, 'INT', null)
+ formatMeasure(count, MetricType.Integer, null),
+ formatMeasure(total, MetricType.Integer, null)
)
- : translateWithParameters('x_show', formatMeasure(count, 'INT', null))}
+ : translateWithParameters('x_show', formatMeasure(count, MetricType.Integer, null))}
</span>
{button}
- {<DeferredSpinner loading={loading} className="text-bottom spacer-left position-absolute" />}
- </div>
+ {<DeferredSpinner loading={loading} className="sw-ml-2" />}
+ </StyledDiv>
);
}
+
+const StyledDiv = withTheme(styled.div`
+ color: ${themeColor('pageContentLight')};
+`);
* 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 { screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
import * as React from 'react';
-import { click } from '../../../helpers/testUtils';
-import { Button } from '../buttons';
+import { renderComponent } from '../../../helpers/testReactTestingUtils';
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'
- );
-});
+describe('ListFooter', () => {
+ describe('rendering', () => {
+ it('should render correctly when loading', async () => {
+ renderListFooter({ loading: true });
+ expect(await screen.findByText('loading')).toBeInTheDocument();
+ });
-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 not render if there are neither loadmore nor reload props', () => {
+ renderListFooter({ loadMore: undefined, reload: undefined });
+ expect(screen.queryByRole('button')).not.toBeInTheDocument();
+ });
-it('should properly call loadMore', () => {
- const loadMore = jest.fn();
- const wrapper = shallowRender({ loadMore });
- click(wrapper.find(Button));
- expect(loadMore).toHaveBeenCalled();
-});
+ it.each([
+ [undefined, 60, 30, true],
+ [undefined, 45, 30, false],
+ [undefined, 60, undefined, false],
+ [60, 60, 30, false],
+ ])(
+ 'should handle showing load more button based on total, count and pageSize',
+ (total, count, pageSize, expected) => {
+ renderListFooter({ total, count, pageSize });
+
+ /* eslint-disable jest/no-conditional-in-test */
+ /* eslint-disable jest/no-conditional-expect */
+ if (expected) {
+ expect(screen.getByRole('button')).toBeInTheDocument();
+ } else {
+ expect(screen.queryByRole('button')).not.toBeInTheDocument();
+ }
+ /* eslint-enable jest/no-conditional-in-test */
+ /* eslint-enable jest/no-conditional-expect */
+ }
+ );
+ });
+
+ it('should properly call load more callback', async () => {
+ const user = userEvent.setup();
+ const loadMore = jest.fn();
+ renderListFooter({ loadMore });
-it('should properly call reload', () => {
- const reload = jest.fn();
- const wrapper = shallowRender({ needReload: true, reload });
- click(wrapper.find(Button));
- expect(reload).toHaveBeenCalled();
+ await user.click(screen.getByRole('button'));
+ expect(loadMore).toHaveBeenCalled();
+ });
+
+ it('should properly call reload callback', async () => {
+ const user = userEvent.setup();
+ const reload = jest.fn();
+ renderListFooter({ needReload: true, reload });
+
+ await user.click(screen.getByRole('button'));
+ expect(reload).toHaveBeenCalled();
+ });
+
+ function renderListFooter(props: Partial<ListFooterProps> = {}) {
+ return renderComponent(<ListFooter count={3} loadMore={jest.fn()} total={5} {...props} />);
+ }
});
-function shallowRender(props: Partial<ListFooterProps> = {}) {
- return shallow<ListFooterProps>(
- <ListFooter count={3} loadMore={jest.fn()} total={5} {...props} />
- );
-}
+// Once the MIUI buttons become the norm, we can use only the above test "suite" and drop this one.
+describe('ListFooter using MIUI buttons', () => {
+ describe('rendering', () => {
+ it('should render correctly when loading', async () => {
+ renderListFooter({ loading: true });
+ expect(await screen.findByText('loading')).toBeInTheDocument();
+ });
+
+ it('should not render if there are neither loadmore nor reload props', () => {
+ renderListFooter({ loadMore: undefined, reload: undefined });
+ expect(screen.queryByRole('button')).not.toBeInTheDocument();
+ });
+
+ it.each([
+ [undefined, 60, 30, true],
+ [undefined, 45, 30, false],
+ [undefined, 60, undefined, false],
+ [60, 60, 30, false],
+ ])(
+ 'should handle showing load more button based on total, count and pageSize',
+ (total, count, pageSize, expected) => {
+ renderListFooter({ total, count, pageSize });
+
+ /* eslint-disable jest/no-conditional-in-test */
+ /* eslint-disable jest/no-conditional-expect */
+ if (expected) {
+ expect(screen.getByRole('button')).toBeInTheDocument();
+ } else {
+ expect(screen.queryByRole('button')).not.toBeInTheDocument();
+ }
+ /* eslint-enable jest/no-conditional-in-test */
+ /* eslint-enable jest/no-conditional-expect */
+ }
+ );
+ });
+
+ it('should properly call load more callback', async () => {
+ const user = userEvent.setup();
+ const loadMore = jest.fn();
+ renderListFooter({ loadMore });
+
+ await user.click(screen.getByRole('button'));
+ expect(loadMore).toHaveBeenCalled();
+ });
+
+ it('should properly call reload callback', async () => {
+ const user = userEvent.setup();
+ const reload = jest.fn();
+ renderListFooter({ needReload: true, reload });
+
+ await user.click(screen.getByRole('button'));
+ expect(reload).toHaveBeenCalled();
+ });
+
+ function renderListFooter(props: Partial<ListFooterProps> = {}) {
+ return renderComponent(
+ <ListFooter count={3} loadMore={jest.fn()} total={5} useMIUIButtons={true} {...props} />
+ );
+ }
+});
+++ /dev/null
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly: default 1`] = `
-<div
- className="list-footer spacer-top note text-center"
- tabIndex={-1}
->
- <span
- aria-busy={false}
- aria-live="polite"
- >
- x_of_y_shown.3.5
- </span>
- <Button
- className="spacer-left"
- data-test="show-more"
- disabled={false}
- onClick={[Function]}
- >
- show_more
- </Button>
- <DeferredSpinner
- className="text-bottom spacer-left position-absolute"
- loading={false}
- />
-</div>
-`;
-
-exports[`should render correctly: empty if everything is loaded 1`] = `
-<div
- className="list-footer spacer-top note text-center"
- tabIndex={-1}
->
- <span
- aria-busy={false}
- aria-live="polite"
- >
- x_of_y_shown.5.5
- </span>
- <DeferredSpinner
- className="text-bottom spacer-left position-absolute"
- loading={false}
- />
-</div>
-`;
-
-exports[`should render correctly: empty if no loadMore nor reload props 1`] = `
-<div
- className="list-footer spacer-top note text-center"
- tabIndex={-1}
->
- <span
- aria-busy={false}
- aria-live="polite"
- >
- x_of_y_shown.3.5
- </span>
- <DeferredSpinner
- className="text-bottom spacer-left position-absolute"
- loading={false}
- />
-</div>
-`;
-
-exports[`should render correctly: force show load more button if count % pageSize is 0, and total is undefined 1`] = `
-<div
- className="list-footer spacer-top note text-center"
- tabIndex={-1}
->
- <span
- aria-busy={false}
- aria-live="polite"
- >
- x_show.60
- </span>
- <Button
- className="spacer-left"
- data-test="show-more"
- disabled={false}
- onClick={[Function]}
- >
- show_more
- </Button>
- <DeferredSpinner
- className="text-bottom spacer-left position-absolute"
- loading={false}
- />
-</div>
-`;
-
-exports[`should render correctly: loading 1`] = `
-<div
- className="list-footer spacer-top note text-center"
- tabIndex={-1}
->
- <span
- aria-busy={true}
- aria-live="polite"
- >
- x_of_y_shown.3.5
- </span>
- <Button
- className="spacer-left"
- data-test="show-more"
- disabled={true}
- onClick={[Function]}
- >
- show_more
- </Button>
- <DeferredSpinner
- className="text-bottom spacer-left position-absolute"
- loading={true}
- />
-</div>
-`;
-
-exports[`should render correctly: reload 1`] = `
-<div
- className="list-footer spacer-top note text-center"
- tabIndex={-1}
->
- <span
- aria-busy={false}
- aria-live="polite"
- >
- x_of_y_shown.3.5
- </span>
- <Button
- className="spacer-left"
- data-test="reload"
- disabled={false}
- onClick={[MockFunction]}
- >
- reload
- </Button>
- <DeferredSpinner
- className="text-bottom spacer-left position-absolute"
- loading={false}
- />
-</div>
-`;
-
-exports[`should render correctly: reload, loading 1`] = `
-<div
- className="list-footer spacer-top note text-center"
- tabIndex={-1}
->
- <span
- aria-busy={true}
- aria-live="polite"
- >
- x_of_y_shown.3.5
- </span>
- <Button
- className="spacer-left"
- data-test="reload"
- disabled={true}
- onClick={[MockFunction]}
- >
- reload
- </Button>
- <DeferredSpinner
- className="text-bottom spacer-left position-absolute"
- loading={true}
- />
-</div>
-`;
-
-exports[`should render correctly: total undefined 1`] = `
-<div
- className="list-footer spacer-top note text-center"
- tabIndex={-1}
->
- <span
- aria-busy={false}
- aria-live="polite"
- >
- x_show.3
- </span>
- <DeferredSpinner
- className="text-bottom spacer-left position-absolute"
- loading={false}
- />
-</div>
-`;