test: Pagination component (#13277)
* Moving Pagination.tsx to the Pagination folder and splitting components into diferent files
* Tests for Pagination components
* replace fireEvent for user-event
* Fix: Pagination references
* Rename PaginationButtonProps file
* Exchanging render imports
* pagination with attribute components
* switch "PaginationButtonProps.ts" to "types.ts"
* remove "Pagination.PaginationWrapper" from ListViewPagination.tsx
* fix: removing PaginationWrapper from tests
* removing data-test-id
* update PaginationProps
diff --git a/superset-frontend/src/components/Pagination.tsx b/superset-frontend/src/components/Pagination.tsx
deleted file mode 100644
index 6a9a746..0000000
--- a/superset-frontend/src/components/Pagination.tsx
+++ /dev/null
@@ -1,137 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-import React, { PureComponent } from 'react';
-import cx from 'classnames';
-import { styled } from '@superset-ui/core';
-
-interface PaginationButton {
- disabled?: boolean;
- onClick: React.EventHandler<React.SyntheticEvent<HTMLElement>>;
-}
-
-interface PaginationItemButton extends PaginationButton {
- active: boolean;
- children: React.ReactNode;
-}
-
-function Prev({ disabled, onClick }: PaginationButton) {
- return (
- <li className={cx({ disabled })}>
- <span role="button" tabIndex={disabled ? -1 : 0} onClick={onClick}>
- «
- </span>
- </li>
- );
-}
-
-function Next({ disabled, onClick }: PaginationButton) {
- return (
- <li className={cx({ disabled })}>
- <span role="button" tabIndex={disabled ? -1 : 0} onClick={onClick}>
- »
- </span>
- </li>
- );
-}
-
-function Item({ active, children, onClick }: PaginationItemButton) {
- return (
- <li className={cx({ active })}>
- <span role="button" tabIndex={active ? -1 : 0} onClick={onClick}>
- {children}
- </span>
- </li>
- );
-}
-
-function Ellipsis({ disabled, onClick }: PaginationButton) {
- return (
- <li className={cx({ disabled })}>
- <span role="button" tabIndex={disabled ? -1 : 0} onClick={onClick}>
- …
- </span>
- </li>
- );
-}
-
-interface PaginationProps {
- children: React.ReactNode;
-}
-
-const PaginationList = styled.ul`
- display: inline-block;
- margin: 16px 0;
- padding: 0;
-
- li {
- display: inline;
- margin: 0 4px;
-
- span {
- padding: 8px 12px;
- text-decoration: none;
- background-color: ${({ theme }) => theme.colors.grayscale.light5};
- border-radius: ${({ theme }) => theme.borderRadius}px;
-
- &:hover,
- &:focus {
- z-index: 2;
- color: ${({ theme }) => theme.colors.grayscale.dark1};
- background-color: ${({ theme }) => theme.colors.grayscale.light3};
- }
- }
-
- &.disabled {
- span {
- background-color: transparent;
- cursor: default;
-
- &:focus {
- outline: none;
- }
- }
- }
- &.active {
- span {
- z-index: 3;
- color: ${({ theme }) => theme.colors.grayscale.light5};
- cursor: default;
- background-color: ${({ theme }) => theme.colors.primary.base};
-
- &:focus {
- outline: none;
- }
- }
- }
- }
-`;
-
-export default class Pagination extends PureComponent<PaginationProps> {
- static Next = Next;
-
- static Prev = Prev;
-
- static Item = Item;
-
- static Ellipsis = Ellipsis;
-
- render() {
- return <PaginationList> {this.props.children}</PaginationList>;
- }
-}
diff --git a/superset-frontend/src/components/Pagination/Ellipsis.test.tsx b/superset-frontend/src/components/Pagination/Ellipsis.test.tsx
new file mode 100644
index 0000000..430924e
--- /dev/null
+++ b/superset-frontend/src/components/Pagination/Ellipsis.test.tsx
@@ -0,0 +1,39 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import React from 'react';
+import { render, screen } from 'spec/helpers/testing-library';
+import userEvent from '@testing-library/user-event';
+import { Ellipsis } from './Ellipsis';
+
+test('Ellipsis - click when the button is enabled', () => {
+ const click = jest.fn();
+ render(<Ellipsis onClick={click} />);
+ expect(click).toBeCalledTimes(0);
+ userEvent.click(screen.getByRole('button'));
+ expect(click).toBeCalledTimes(1);
+});
+
+test('Ellipsis - click when the button is disabled', () => {
+ const click = jest.fn();
+ render(<Ellipsis onClick={click} disabled />);
+ expect(click).toBeCalledTimes(0);
+ userEvent.click(screen.getByRole('button'));
+ expect(click).toBeCalledTimes(0);
+});
diff --git a/superset-frontend/src/components/Pagination/Ellipsis.tsx b/superset-frontend/src/components/Pagination/Ellipsis.tsx
new file mode 100644
index 0000000..5926702
--- /dev/null
+++ b/superset-frontend/src/components/Pagination/Ellipsis.tsx
@@ -0,0 +1,39 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import React from 'react';
+import classNames from 'classnames';
+import { PaginationButtonProps } from './types';
+
+export function Ellipsis({ disabled, onClick }: PaginationButtonProps) {
+ return (
+ <li className={classNames({ disabled })}>
+ <span
+ role="button"
+ tabIndex={disabled ? -1 : 0}
+ onClick={e => {
+ e.preventDefault();
+ if (!disabled) onClick(e);
+ }}
+ >
+ …
+ </span>
+ </li>
+ );
+}
diff --git a/superset-frontend/src/components/Pagination/Item.test.tsx b/superset-frontend/src/components/Pagination/Item.test.tsx
new file mode 100644
index 0000000..a942276
--- /dev/null
+++ b/superset-frontend/src/components/Pagination/Item.test.tsx
@@ -0,0 +1,49 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import React from 'react';
+import { render, screen } from 'spec/helpers/testing-library';
+import userEvent from '@testing-library/user-event';
+import { Item } from './Item';
+
+test('Item - click when the item is not active', () => {
+ const click = jest.fn();
+ render(
+ <Item onClick={click}>
+ <div data-test="test" />
+ </Item>,
+ );
+ expect(click).toBeCalledTimes(0);
+ userEvent.click(screen.getByRole('button'));
+ expect(click).toBeCalledTimes(1);
+ expect(screen.getByTestId('test')).toBeInTheDocument();
+});
+
+test('Item - click when the item is active', () => {
+ const click = jest.fn();
+ render(
+ <Item onClick={click} active>
+ <div data-test="test" />
+ </Item>,
+ );
+ expect(click).toBeCalledTimes(0);
+ userEvent.click(screen.getByRole('button'));
+ expect(click).toBeCalledTimes(0);
+ expect(screen.getByTestId('test')).toBeInTheDocument();
+});
diff --git a/superset-frontend/src/components/Pagination/Item.tsx b/superset-frontend/src/components/Pagination/Item.tsx
new file mode 100644
index 0000000..78c5abc
--- /dev/null
+++ b/superset-frontend/src/components/Pagination/Item.tsx
@@ -0,0 +1,44 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import React from 'react';
+import classNames from 'classnames';
+import { PaginationButtonProps } from './types';
+
+interface PaginationItemButton extends PaginationButtonProps {
+ active?: boolean;
+ children: React.ReactNode;
+}
+
+export function Item({ active, children, onClick }: PaginationItemButton) {
+ return (
+ <li className={classNames({ active })}>
+ <span
+ role="button"
+ tabIndex={active ? -1 : 0}
+ onClick={e => {
+ e.preventDefault();
+ if (!active) onClick(e);
+ }}
+ >
+ {children}
+ </span>
+ </li>
+ );
+}
diff --git a/superset-frontend/src/components/Pagination/Next.test.tsx b/superset-frontend/src/components/Pagination/Next.test.tsx
new file mode 100644
index 0000000..9daddec
--- /dev/null
+++ b/superset-frontend/src/components/Pagination/Next.test.tsx
@@ -0,0 +1,39 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import React from 'react';
+import { render, screen } from 'spec/helpers/testing-library';
+import userEvent from '@testing-library/user-event';
+import { Next } from './Next';
+
+test('Next - click when the button is enabled', () => {
+ const click = jest.fn();
+ render(<Next onClick={click} />);
+ expect(click).toBeCalledTimes(0);
+ userEvent.click(screen.getByRole('button'));
+ expect(click).toBeCalledTimes(1);
+});
+
+test('Next - click when the button is disabled', () => {
+ const click = jest.fn();
+ render(<Next onClick={click} disabled />);
+ expect(click).toBeCalledTimes(0);
+ userEvent.click(screen.getByRole('button'));
+ expect(click).toBeCalledTimes(0);
+});
diff --git a/superset-frontend/src/components/Pagination/Next.tsx b/superset-frontend/src/components/Pagination/Next.tsx
new file mode 100644
index 0000000..838f38a
--- /dev/null
+++ b/superset-frontend/src/components/Pagination/Next.tsx
@@ -0,0 +1,39 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import React from 'react';
+import classNames from 'classnames';
+import { PaginationButtonProps } from './types';
+
+export function Next({ disabled, onClick }: PaginationButtonProps) {
+ return (
+ <li className={classNames({ disabled })}>
+ <span
+ role="button"
+ tabIndex={disabled ? -1 : 0}
+ onClick={e => {
+ e.preventDefault();
+ if (!disabled) onClick(e);
+ }}
+ >
+ »
+ </span>
+ </li>
+ );
+}
diff --git a/superset-frontend/src/components/Pagination/Pagination.test.tsx b/superset-frontend/src/components/Pagination/Pagination.test.tsx
new file mode 100644
index 0000000..192d4de
--- /dev/null
+++ b/superset-frontend/src/components/Pagination/Pagination.test.tsx
@@ -0,0 +1,69 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import React from 'react';
+import { render, screen } from 'spec/helpers/testing-library';
+import Pagination from '.';
+
+jest.mock('./Next', () => ({
+ Next: () => <div data-test="next" />,
+}));
+jest.mock('./Prev', () => ({
+ Prev: () => <div data-test="prev" />,
+}));
+jest.mock('./Item', () => ({
+ Item: () => <div data-test="item" />,
+}));
+jest.mock('./Ellipsis', () => ({
+ Ellipsis: () => <div data-test="ellipsis" />,
+}));
+
+test('Pagination rendering correctly', () => {
+ render(
+ <Pagination>
+ <li data-test="test" />
+ </Pagination>,
+ );
+ expect(screen.getByRole('navigation')).toBeInTheDocument();
+ expect(screen.getByTestId('test')).toBeInTheDocument();
+});
+
+test('Next attribute', () => {
+ render(<Pagination.Next onClick={jest.fn()} />);
+ expect(screen.getByTestId('next')).toBeInTheDocument();
+});
+
+test('Prev attribute', () => {
+ render(<Pagination.Next onClick={jest.fn()} />);
+ expect(screen.getByTestId('next')).toBeInTheDocument();
+});
+
+test('Item attribute', () => {
+ render(
+ <Pagination.Item onClick={jest.fn()}>
+ <></>
+ </Pagination.Item>,
+ );
+ expect(screen.getByTestId('item')).toBeInTheDocument();
+});
+
+test('Ellipsis attribute', () => {
+ render(<Pagination.Ellipsis onClick={jest.fn()} />);
+ expect(screen.getByTestId('ellipsis')).toBeInTheDocument();
+});
diff --git a/superset-frontend/src/components/Pagination/Prev.test.tsx b/superset-frontend/src/components/Pagination/Prev.test.tsx
new file mode 100644
index 0000000..924a9e9
--- /dev/null
+++ b/superset-frontend/src/components/Pagination/Prev.test.tsx
@@ -0,0 +1,39 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import React from 'react';
+import { render, screen } from 'spec/helpers/testing-library';
+import userEvent from '@testing-library/user-event';
+import { Prev } from './Prev';
+
+test('Prev - click when the button is enabled', () => {
+ const click = jest.fn();
+ render(<Prev onClick={click} />);
+ expect(click).toBeCalledTimes(0);
+ userEvent.click(screen.getByRole('button'));
+ expect(click).toBeCalledTimes(1);
+});
+
+test('Prev - click when the button is disabled', () => {
+ const click = jest.fn();
+ render(<Prev onClick={click} disabled />);
+ expect(click).toBeCalledTimes(0);
+ userEvent.click(screen.getByRole('button'));
+ expect(click).toBeCalledTimes(0);
+});
diff --git a/superset-frontend/src/components/Pagination/Prev.tsx b/superset-frontend/src/components/Pagination/Prev.tsx
new file mode 100644
index 0000000..678b493
--- /dev/null
+++ b/superset-frontend/src/components/Pagination/Prev.tsx
@@ -0,0 +1,39 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import React from 'react';
+import classNames from 'classnames';
+import { PaginationButtonProps } from './types';
+
+export function Prev({ disabled, onClick }: PaginationButtonProps) {
+ return (
+ <li className={classNames({ disabled })}>
+ <span
+ role="button"
+ tabIndex={disabled ? -1 : 0}
+ onClick={e => {
+ e.preventDefault();
+ if (!disabled) onClick(e);
+ }}
+ >
+ «
+ </span>
+ </li>
+ );
+}
diff --git a/superset-frontend/src/components/Pagination/index.tsx b/superset-frontend/src/components/Pagination/index.tsx
new file mode 100644
index 0000000..cff503d
--- /dev/null
+++ b/superset-frontend/src/components/Pagination/index.tsx
@@ -0,0 +1,88 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import React from 'react';
+import { styled } from '@superset-ui/core';
+import { Next } from './Next';
+import { Prev } from './Prev';
+import { Item } from './Item';
+import { Ellipsis } from './Ellipsis';
+
+interface PaginationProps {
+ children: JSX.Element | JSX.Element[];
+}
+
+const PaginationList = styled.ul`
+ display: inline-block;
+ margin: 16px 0;
+ padding: 0;
+
+ li {
+ display: inline;
+ margin: 0 4px;
+
+ span {
+ padding: 8px 12px;
+ text-decoration: none;
+ background-color: ${({ theme }) => theme.colors.grayscale.light5};
+ border-radius: ${({ theme }) => theme.borderRadius}px;
+
+ &:hover,
+ &:focus {
+ z-index: 2;
+ color: ${({ theme }) => theme.colors.grayscale.dark1};
+ background-color: ${({ theme }) => theme.colors.grayscale.light3};
+ }
+ }
+
+ &.disabled {
+ span {
+ background-color: transparent;
+ cursor: default;
+
+ &:focus {
+ outline: none;
+ }
+ }
+ }
+ &.active {
+ span {
+ z-index: 3;
+ color: ${({ theme }) => theme.colors.grayscale.light5};
+ cursor: default;
+ background-color: ${({ theme }) => theme.colors.primary.base};
+
+ &:focus {
+ outline: none;
+ }
+ }
+ }
+ }
+`;
+
+function Pagination({ children }: PaginationProps) {
+ return <PaginationList role="navigation">{children}</PaginationList>;
+}
+
+Pagination.Next = Next;
+Pagination.Prev = Prev;
+Pagination.Item = Item;
+Pagination.Ellipsis = Ellipsis;
+
+export default Pagination;
diff --git a/superset-frontend/src/components/Pagination/types.ts b/superset-frontend/src/components/Pagination/types.ts
new file mode 100644
index 0000000..fb9c97b
--- /dev/null
+++ b/superset-frontend/src/components/Pagination/types.ts
@@ -0,0 +1,23 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+export interface PaginationButtonProps {
+ disabled?: boolean;
+ onClick: React.EventHandler<React.SyntheticEvent<HTMLElement>>;
+}