Skip to content

Commit ed3fee7

Browse files
committed
chore(Pagination): use React.forwardRef() (#4255)
1 parent 73b8582 commit ed3fee7

File tree

4 files changed

+95
-92
lines changed

4 files changed

+95
-92
lines changed

src/addons/Pagination/Pagination.js

Lines changed: 55 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -3,79 +3,83 @@ import PropTypes from 'prop-types'
33
import React from 'react'
44

55
import {
6-
ModernAutoControlledComponent as Component,
76
createPaginationItems,
87
customPropTypes,
98
getUnhandledProps,
9+
useAutoControlledValue,
1010
} from '../../lib'
1111
import Menu from '../../collections/Menu'
1212
import PaginationItem from './PaginationItem'
1313

1414
/**
1515
* A component to render a pagination.
1616
*/
17-
export default class Pagination extends Component {
18-
getInitialAutoControlledState() {
19-
return { activePage: 1 }
20-
}
17+
const Pagination = React.forwardRef(function (props, ref) {
18+
const {
19+
'aria-label': ariaLabel,
20+
boundaryRange,
21+
disabled,
22+
ellipsisItem,
23+
siblingRange,
24+
totalPages,
25+
} = props
26+
const [activePage, setActivePage] = useAutoControlledValue({
27+
state: props.activePage,
28+
defaultState: props.defaultActivePage,
29+
initialState: 1,
30+
})
2131

22-
handleItemClick = (e, { value: nextActivePage }) => {
23-
const { activePage: prevActivePage } = this.state
32+
const handleItemClick = (e, { value: nextActivePage }) => {
33+
const prevActivePage = activePage
2434

2535
// Heads up! We need the cast to the "number" type there, as `activePage` can be a string
26-
if (+prevActivePage === +nextActivePage) return
36+
if (+prevActivePage === +nextActivePage) {
37+
return
38+
}
2739

28-
this.setState({ activePage: nextActivePage })
29-
_.invoke(this.props, 'onPageChange', e, { ...this.props, activePage: nextActivePage })
40+
setActivePage(nextActivePage)
41+
_.invoke(props, 'onPageChange', e, { ...props, activePage: nextActivePage })
3042
}
3143

32-
handleItemOverrides = (active, type, value) => (predefinedProps) => ({
44+
const handleItemOverrides = (active, type, value) => (predefinedProps) => ({
3345
active,
3446
type,
3547
key: `${type}-${value}`,
3648
onClick: (e, itemProps) => {
3749
_.invoke(predefinedProps, 'onClick', e, itemProps)
38-
if (itemProps.type !== 'ellipsisItem') this.handleItemClick(e, itemProps)
50+
51+
if (itemProps.type !== 'ellipsisItem') {
52+
handleItemClick(e, itemProps)
53+
}
3954
},
4055
})
4156

42-
render() {
43-
const {
44-
'aria-label': ariaLabel,
45-
boundaryRange,
46-
disabled,
47-
ellipsisItem,
48-
siblingRange,
49-
totalPages,
50-
} = this.props
51-
const { activePage } = this.state
52-
53-
const items = createPaginationItems({
54-
activePage,
55-
boundaryRange,
56-
hideEllipsis: _.isNil(ellipsisItem),
57-
siblingRange,
58-
totalPages,
59-
})
60-
const rest = getUnhandledProps(Pagination, this.props)
61-
62-
return (
63-
<Menu {...rest} aria-label={ariaLabel} pagination role='navigation'>
64-
{_.map(items, ({ active, type, value }) =>
65-
PaginationItem.create(this.props[type], {
66-
defaultProps: {
67-
content: value,
68-
disabled,
69-
value,
70-
},
71-
overrideProps: this.handleItemOverrides(active, type, value),
72-
}),
73-
)}
74-
</Menu>
75-
)
76-
}
77-
}
78-
57+
const items = createPaginationItems({
58+
activePage,
59+
boundaryRange,
60+
hideEllipsis: _.isNil(ellipsisItem),
61+
siblingRange,
62+
totalPages,
63+
})
64+
const rest = getUnhandledProps(Pagination, props)
65+
66+
return (
67+
<Menu {...rest} aria-label={ariaLabel} pagination role='navigation' ref={ref}>
68+
{_.map(items, ({ active, type, value }) =>
69+
PaginationItem.create(props[type], {
70+
defaultProps: {
71+
content: value,
72+
disabled,
73+
value,
74+
},
75+
overrideProps: handleItemOverrides(active, type, value),
76+
}),
77+
)}
78+
</Menu>
79+
)
80+
})
81+
82+
Pagination.displayName = 'Pagination'
7983
Pagination.propTypes = {
8084
/** A pagination item can have an aria label. */
8185
'aria-label': PropTypes.string,
@@ -125,8 +129,6 @@ Pagination.propTypes = {
125129
totalPages: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired,
126130
}
127131

128-
Pagination.autoControlledProps = ['activePage']
129-
130132
Pagination.defaultProps = {
131133
'aria-label': 'Pagination Navigation',
132134
boundaryRange: 1,
@@ -152,3 +154,5 @@ Pagination.defaultProps = {
152154
}
153155

154156
Pagination.Item = PaginationItem
157+
158+
export default Pagination

src/addons/Pagination/PaginationItem.js

Lines changed: 28 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,47 @@
11
import keyboardKey from 'keyboard-key'
22
import _ from 'lodash'
33
import PropTypes from 'prop-types'
4-
import { Component } from 'react'
4+
import * as React from 'react'
55

66
import { createShorthandFactory } from '../../lib'
77
import MenuItem from '../../collections/Menu/MenuItem'
88

99
/**
1010
* An item of a pagination.
1111
*/
12-
class PaginationItem extends Component {
13-
handleClick = (e) => {
14-
_.invoke(this.props, 'onClick', e, this.props)
15-
}
12+
const PaginationItem = React.forwardRef(function (props, ref) {
13+
const { active, type } = props
14+
const disabled = props.disabled || type === 'ellipsisItem'
1615

17-
handleKeyDown = (e) => {
18-
_.invoke(this.props, 'onKeyDown', e, this.props)
19-
if (keyboardKey.getCode(e) === keyboardKey.Enter) _.invoke(this.props, 'onClick', e, this.props)
16+
const handleClick = (e) => {
17+
_.invoke(props, 'onClick', e, props)
2018
}
2119

22-
handleOverrides = () => ({
23-
onClick: this.handleClick,
24-
onKeyDown: this.handleKeyDown,
25-
})
26-
27-
render() {
28-
const { active, type } = this.props
29-
const disabled = this.props.disabled || type === 'ellipsisItem'
20+
const handleKeyDown = (e) => {
21+
_.invoke(props, 'onKeyDown', e, props)
3022

31-
return MenuItem.create(this.props, {
32-
defaultProps: {
33-
active,
34-
'aria-current': active,
35-
'aria-disabled': disabled,
36-
disabled,
37-
onClick: this.handleClick,
38-
onKeyDown: this.handleKeyDown,
39-
tabIndex: disabled ? -1 : 0,
40-
},
41-
overrideProps: this.handleOverrides,
42-
})
23+
if (keyboardKey.getCode(e) === keyboardKey.Enter) {
24+
_.invoke(props, 'onClick', e, props)
25+
}
4326
}
44-
}
4527

28+
return MenuItem.create(props, {
29+
defaultProps: {
30+
active,
31+
'aria-current': active,
32+
'aria-disabled': disabled,
33+
disabled,
34+
tabIndex: disabled ? -1 : 0,
35+
},
36+
overrideProps: () => ({
37+
onClick: handleClick,
38+
onKeyDown: handleKeyDown,
39+
ref,
40+
}),
41+
})
42+
})
43+
44+
PaginationItem.displayName = 'PaginationItem'
4645
PaginationItem.propTypes = {
4746
/** A pagination item can be active. */
4847
active: PropTypes.bool,

test/specs/addons/Pagination/Pagination-test.js

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@ import PaginationItem from 'src/addons/Pagination/PaginationItem'
55
import * as common from 'test/specs/commonTests'
66
import { sandbox } from 'test/utils'
77

8+
const requiredProps = {
9+
totalPages: 0,
10+
}
11+
812
describe('Pagination', () => {
9-
common.isConformant(Pagination, {
10-
requiredProps: {
11-
totalPages: 0,
12-
},
13-
})
13+
common.isConformant(Pagination, { requiredProps })
14+
common.forwardsRef(Pagination, { requiredProps, tagName: 'div' })
1415
common.hasSubcomponents(Pagination, [PaginationItem])
1516

1617
describe('disabled', () => {
@@ -24,26 +25,24 @@ describe('Pagination', () => {
2425

2526
describe('onPageChange', () => {
2627
it('is called with (e, data) when clicked on a pagination item', () => {
27-
const event = { target: null }
2828
const onPageChange = sandbox.spy()
2929
const onPageItemClick = sandbox.spy()
3030

31-
mount(
31+
const wrapper = mount(
3232
<Pagination
3333
activePage={1}
3434
onPageChange={onPageChange}
3535
pageItem={{ onClick: onPageItemClick }}
3636
totalPages={3}
3737
/>,
3838
)
39-
.find('PaginationItem')
40-
.at(4)
41-
.simulate('click', event)
39+
40+
wrapper.find('PaginationItem').at(4).simulate('click')
4241

4342
onPageChange.should.have.been.calledOnce()
44-
onPageChange.should.have.been.calledWithMatch(event, { activePage: 3 })
43+
onPageChange.should.have.been.calledWithMatch({ type: 'click' }, { activePage: 3 })
4544
onPageItemClick.should.have.been.calledOnce()
46-
onPageItemClick.should.have.been.calledWithMatch(event, { value: 3 })
45+
onPageItemClick.should.have.been.calledWithMatch({ type: 'click' }, { value: 3 })
4746
})
4847

4948
it('will be omitted if occurred for the same pagination item as the current', () => {

test/specs/addons/Pagination/PaginationItem-test.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { sandbox } from 'test/utils'
66

77
describe('PaginationItem', () => {
88
common.isConformant(PaginationItem)
9+
common.forwardsRef(PaginationItem, { tagName: 'a' })
910
common.implementsCreateMethod(PaginationItem)
1011

1112
describe('active', () => {

0 commit comments

Comments
 (0)