Skip to content

Commit e6e5a7b

Browse files
committed
fix(shared-types,ui-pagination): pagination indicators have spacing and coded as a list for a11y
1 parent d91de1c commit e6e5a7b

File tree

7 files changed

+83
-19
lines changed

7 files changed

+83
-19
lines changed

packages/shared-types/src/ComponentThemeVariables.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -949,6 +949,10 @@ export type PaginationPageInputTheme = {
949949
inputWidth: string
950950
}
951951

952+
export type PaginationTheme = {
953+
pageIndicatorGap: Spacing['xSmall']
954+
}
955+
952956
export type PillTheme = {
953957
fontFamily: Typography['fontFamily']
954958
padding: string | 0

packages/ui-pagination/src/Pagination/PaginationButton/index.tsx

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -59,17 +59,20 @@ class PaginationButton extends Component<PaginationPageProps> {
5959

6060
const props = omitProps(this.props, PaginationButton.allowedProps, exclude)
6161

62+
// wrapped in an unstyled <li> for better a11y
6263
return (
63-
<BaseButton
64-
color="primary"
65-
withBackground={this.props.current}
66-
withBorder={this.props.current}
67-
{...props}
68-
aria-current={this.props.current ? 'page' : undefined}
69-
elementRef={this.handleRef}
70-
>
71-
{this.props.children}
72-
</BaseButton>
64+
<li style={{ all: 'unset' }}>
65+
<BaseButton
66+
color="primary"
67+
withBackground={this.props.current}
68+
withBorder={this.props.current}
69+
{...props}
70+
aria-current={this.props.current ? 'page' : undefined}
71+
elementRef={this.handleRef}
72+
>
73+
{this.props.children}
74+
</BaseButton>
75+
</li>
7376
)
7477
}
7578
}

packages/ui-pagination/src/Pagination/__new-tests__/Pagination.test.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,9 @@ describe('<Pagination />', () => {
123123
it('by default', async () => {
124124
const { container } = render(
125125
<Pagination variant="compact" labelNext="Next" labelPrev="Prev">
126-
{buildPages(5)}
126+
<ul>
127+
{buildPages(5)}
128+
</ul>
127129
</Pagination>
128130
)
129131

@@ -134,7 +136,7 @@ describe('<Pagination />', () => {
134136
it('by default with more pages', async () => {
135137
const { container } = render(
136138
<Pagination variant="compact" labelNext="Next" labelPrev="Prev">
137-
{buildPages(8)}
139+
<ul>{buildPages(8)}</ul>
138140
</Pagination>
139141
)
140142
const axeCheck = await runAxeCheck(container)
@@ -151,7 +153,7 @@ describe('<Pagination />', () => {
151153
labelLast="Last"
152154
withFirstAndLastButton
153155
>
154-
{buildPages(8)}
156+
<ul>{buildPages(8)}</ul>
155157
</Pagination>
156158
)
157159
const axeCheck = await runAxeCheck(container)
@@ -169,7 +171,7 @@ describe('<Pagination />', () => {
169171
withFirstAndLastButton
170172
showDisabledButtons
171173
>
172-
{buildPages(8)}
174+
<ul>{buildPages(8)}</ul>
173175
</Pagination>
174176
)
175177
const axeCheck = await runAxeCheck(container)

packages/ui-pagination/src/Pagination/index.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import { PaginationArrowButton } from './PaginationArrowButton'
3636
import { PaginationPageInput } from './PaginationPageInput'
3737

3838
import generateStyle from './styles'
39+
import generateComponentTheme from './theme'
3940

4041
import type { PaginationPageProps } from './PaginationButton/props'
4142
import type { PaginationArrowDirections } from './PaginationArrowButton/props'
@@ -80,7 +81,7 @@ category: components
8081
---
8182
**/
8283
@withDeterministicId()
83-
@withStyle(generateStyle, null)
84+
@withStyle(generateStyle, generateComponentTheme)
8485
@testable()
8586
class Pagination extends Component<PaginationProps> {
8687
static readonly componentId = 'Pagination'
@@ -103,7 +104,7 @@ class Pagination extends Component<PaginationProps> {
103104
currentPage: 1,
104105
siblingCount: 1,
105106
boundaryCount: 1,
106-
ellipsis: '...',
107+
ellipsis: <li style={{all: 'unset'}}>...</li>,
107108
renderPageIndicator: (page: number) => page
108109
}
109110

@@ -408,7 +409,7 @@ class Pagination extends Component<PaginationProps> {
408409
)
409410
)
410411
}
411-
return pages
412+
return (<ul css={this.props.styles?.pageIndicatorList}>{pages}</ul>)
412413
}
413414

414415
renderPages(currentPageIndex: number) {

packages/ui-pagination/src/Pagination/props.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,9 @@ type PaginationProps = PaginationOwnProps &
197197
OtherHTMLAttributes<PaginationOwnProps> &
198198
WithDeterministicIdProps
199199

200-
type PaginationStyle = ComponentStyle<'pagination' | 'pages'>
200+
type PaginationStyle = ComponentStyle<
201+
'pagination' | 'pages' | 'pageIndicatorList'
202+
>
201203

202204
const propTypes: PropValidators<PropKeys> = {
203205
children: Children.oneOf([PaginationButton]),

packages/ui-pagination/src/Pagination/styles.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
* SOFTWARE.
2323
*/
2424

25+
import type { PaginationTheme } from '@instructure/shared-types'
2526
import type { PaginationStyle } from './props'
2627

2728
/**
@@ -34,8 +35,14 @@ import type { PaginationStyle } from './props'
3435
* @param {Object} state the state of the component, the style is applied to
3536
* @return {Object} The final style object, which will be used in the component
3637
*/
37-
const generateStyle = (): PaginationStyle => {
38+
const generateStyle = (componentTheme: PaginationTheme): PaginationStyle => {
3839
return {
40+
pageIndicatorList: {
41+
all: 'unset',
42+
display: 'flex',
43+
alignItems: 'center',
44+
gap: componentTheme.pageIndicatorGap
45+
},
3946
pagination: {
4047
label: 'pagination',
4148
textAlign: 'center'
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* The MIT License (MIT)
3+
*
4+
* Copyright (c) 2015 - present Instructure, Inc.
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in all
14+
* copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
* SOFTWARE.
23+
*/
24+
25+
import type { Theme } from '@instructure/ui-themes'
26+
import { PaginationTheme } from '@instructure/shared-types'
27+
28+
/**
29+
* Generates the theme object for the component from the theme and provided additional information
30+
* @param {Object} theme The actual theme object.
31+
* @return {Object} The final theme object with the overrides and component variables
32+
*/
33+
const generateComponentTheme = (theme: Theme): PaginationTheme => {
34+
const { spacing } = theme
35+
36+
const componentVariables: PaginationTheme = {
37+
pageIndicatorGap: spacing.xSmall
38+
}
39+
40+
return {
41+
...componentVariables
42+
}
43+
}
44+
45+
export default generateComponentTheme

0 commit comments

Comments
 (0)