@instructure/ui-pagination 8.13.1-snapshot.9 → 8.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/es/Pagination/PaginationArrowButton/index.js +36 -4
  3. package/es/Pagination/PaginationArrowButton/props.js +6 -3
  4. package/es/Pagination/PaginationButton/props.js +9 -2
  5. package/es/Pagination/PaginationLocator.js +4 -0
  6. package/es/Pagination/PaginationPageInput/PaginationPageInputLocator.js +35 -0
  7. package/es/Pagination/PaginationPageInput/index.js +208 -0
  8. package/es/Pagination/PaginationPageInput/props.js +35 -0
  9. package/es/Pagination/PaginationPageInput/styles.js +55 -0
  10. package/es/Pagination/PaginationPageInput/theme.js +40 -0
  11. package/es/Pagination/index.js +178 -52
  12. package/es/Pagination/props.js +32 -2
  13. package/lib/Pagination/PaginationArrowButton/index.js +39 -3
  14. package/lib/Pagination/PaginationArrowButton/props.js +6 -3
  15. package/lib/Pagination/PaginationButton/props.js +9 -2
  16. package/lib/Pagination/PaginationLocator.js +5 -0
  17. package/lib/Pagination/PaginationPageInput/PaginationPageInputLocator.js +46 -0
  18. package/lib/Pagination/PaginationPageInput/index.js +202 -0
  19. package/lib/Pagination/PaginationPageInput/props.js +46 -0
  20. package/lib/Pagination/PaginationPageInput/styles.js +63 -0
  21. package/lib/Pagination/PaginationPageInput/theme.js +48 -0
  22. package/lib/Pagination/index.js +179 -52
  23. package/lib/Pagination/props.js +32 -2
  24. package/package.json +23 -22
  25. package/src/Pagination/PaginationArrowButton/index.tsx +32 -4
  26. package/src/Pagination/PaginationArrowButton/props.ts +22 -6
  27. package/src/Pagination/PaginationButton/props.ts +18 -2
  28. package/src/Pagination/PaginationLocator.ts +5 -1
  29. package/src/Pagination/PaginationPageInput/PaginationPageInputLocator.ts +42 -0
  30. package/src/Pagination/PaginationPageInput/index.tsx +242 -0
  31. package/src/Pagination/PaginationPageInput/props.ts +112 -0
  32. package/src/Pagination/PaginationPageInput/styles.ts +60 -0
  33. package/src/Pagination/PaginationPageInput/theme.ts +46 -0
  34. package/src/Pagination/README.md +116 -0
  35. package/src/Pagination/index.tsx +213 -80
  36. package/src/Pagination/props.ts +53 -4
  37. package/tsconfig.build.json +26 -2
  38. package/tsconfig.build.tsbuildinfo +1 -0
  39. package/types/Pagination/PaginationArrowButton/PaginationArrowButtonLocator.d.ts +57 -56
  40. package/types/Pagination/PaginationArrowButton/PaginationArrowButtonLocator.d.ts.map +1 -1
  41. package/types/Pagination/PaginationArrowButton/index.d.ts +9 -4
  42. package/types/Pagination/PaginationArrowButton/index.d.ts.map +1 -1
  43. package/types/Pagination/PaginationArrowButton/props.d.ts +5 -3
  44. package/types/Pagination/PaginationArrowButton/props.d.ts.map +1 -1
  45. package/types/Pagination/PaginationButton/index.d.ts +2 -0
  46. package/types/Pagination/PaginationButton/index.d.ts.map +1 -1
  47. package/types/Pagination/PaginationButton/props.d.ts +1 -0
  48. package/types/Pagination/PaginationButton/props.d.ts.map +1 -1
  49. package/types/Pagination/PaginationLocator.d.ts +944 -234
  50. package/types/Pagination/PaginationLocator.d.ts.map +1 -1
  51. package/types/Pagination/PaginationPageInput/PaginationPageInputLocator.d.ts +581 -0
  52. package/types/Pagination/PaginationPageInput/PaginationPageInputLocator.d.ts.map +1 -0
  53. package/types/Pagination/PaginationPageInput/index.d.ts +63 -0
  54. package/types/Pagination/PaginationPageInput/index.d.ts.map +1 -0
  55. package/types/Pagination/PaginationPageInput/props.d.ts +46 -0
  56. package/types/Pagination/PaginationPageInput/props.d.ts.map +1 -0
  57. package/types/Pagination/PaginationPageInput/styles.d.ts +15 -0
  58. package/types/Pagination/PaginationPageInput/styles.d.ts.map +1 -0
  59. package/types/Pagination/PaginationPageInput/theme.d.ts +10 -0
  60. package/types/Pagination/PaginationPageInput/theme.d.ts.map +1 -0
  61. package/types/Pagination/index.d.ts +50 -14
  62. package/types/Pagination/index.d.ts.map +1 -1
  63. package/types/Pagination/props.d.ts +13 -3
  64. package/types/Pagination/props.d.ts.map +1 -1
  65. package/LICENSE.md +0 -27
@@ -34,32 +34,42 @@ import { withStyle, jsx } from '@instructure/emotion'
34
34
 
35
35
  import { PaginationButton } from './PaginationButton'
36
36
  import { PaginationArrowButton } from './PaginationArrowButton'
37
+ import { PaginationPageInput } from './PaginationPageInput'
37
38
 
38
39
  import generateStyle from './styles'
39
40
 
41
+ import type { PaginationPageProps } from './PaginationButton/props'
42
+ import type { PaginationArrowDirections } from './PaginationArrowButton/props'
43
+
40
44
  import { propTypes, allowedProps } from './props'
41
- import type { PaginationProps } from './props'
45
+ import type { PaginationProps, PaginationSnapshot } from './props'
42
46
 
43
47
  /** This is an [].findIndex optimized to work on really big, but sparse, arrays */
44
48
  // @ts-expect-error ts-migrate(7006) FIXME: Parameter 'arr' implicitly has an 'any' type.
45
49
  const fastFindIndex = (arr, fn) =>
46
50
  Number(Object.keys(arr).find((k) => fn(arr[Number(k)])))
47
51
 
48
- // @ts-expect-error ts-migrate(7006) FIXME: Parameter 'props' implicitly has an 'any' type.
49
- function propsHaveCompactView(props) {
50
- return props.variant === 'compact' && props?.children?.length > 5
52
+ const childrenArray = (props: PaginationProps) => {
53
+ const { children } = props
54
+
55
+ if (!children) {
56
+ return []
57
+ }
58
+
59
+ return Array.isArray(children)
60
+ ? (children as React.ReactElement<PaginationPageProps>[])
61
+ : ([children] as React.ReactElement<PaginationPageProps>[])
51
62
  }
52
63
 
53
- // @ts-expect-error ts-migrate(7006) FIXME: Parameter 'props' implicitly has an 'any' type.
54
- function shouldShowPrevButton(props, currentPageIndex) {
55
- return propsHaveCompactView(props) && currentPageIndex > 0
64
+ function propsHaveCompactView(props: PaginationProps) {
65
+ return props.variant === 'compact' && childrenArray(props).length > 5
56
66
  }
57
67
 
58
- // @ts-expect-error ts-migrate(7006) FIXME: Parameter 'props' implicitly has an 'any' type.
59
- function shouldShowNextButton(props, currentPageIndex) {
60
- return (
61
- propsHaveCompactView(props) && currentPageIndex < props.children.length - 1
62
- )
68
+ type ArrowConfig = {
69
+ pageIndex: number
70
+ label: string
71
+ shouldEnableIcon: boolean
72
+ handleButtonRef: (el: HTMLButtonElement) => void
63
73
  }
64
74
 
65
75
  /**
@@ -67,7 +77,6 @@ function shouldShowNextButton(props, currentPageIndex) {
67
77
  category: components
68
78
  ---
69
79
  **/
70
-
71
80
  @withStyle(generateStyle, null)
72
81
  @testable()
73
82
  class Pagination extends Component<PaginationProps> {
@@ -78,31 +87,41 @@ class Pagination extends Component<PaginationProps> {
78
87
  static defaultProps = {
79
88
  children: null,
80
89
  disabled: false,
90
+ withFirstAndLastButton: false,
91
+ showDisabledButtons: false,
81
92
  variant: 'full',
82
93
  as: 'div',
83
94
  // @ts-expect-error ts-migrate(6133) FIXME: 'el' is declared but its value is never read.
84
95
  elementRef: (el) => {},
96
+ labelNumberInput: (numberOfPages: number) => `of ${numberOfPages}`,
97
+ screenReaderLabelNumberInput: (
98
+ currentPage: number,
99
+ numberOfPages: number
100
+ ) => `Select page (${currentPage} of ${numberOfPages})`,
85
101
  shouldHandleFocus: true
86
102
  }
87
103
 
88
104
  static Page = PaginationButton
89
105
  static Navigation = PaginationArrowButton
90
106
 
107
+ _labelId: string
108
+
91
109
  ref: Element | null = null
110
+ _inputRef: Element | null = null
111
+
112
+ _firstButton: HTMLButtonElement | null = null
113
+ _prevButton: HTMLButtonElement | null = null
114
+ _nextButton: HTMLButtonElement | null = null
115
+ _lastButton: HTMLButtonElement | null = null
92
116
 
93
117
  // @ts-expect-error ts-migrate(7019) FIXME: Rest parameter 'args' implicitly has an 'any[]' ty... Remove this comment to see the full error message
94
118
  constructor(...args) {
95
119
  // @ts-expect-error ts-migrate(2556) FIXME: Expected 1-2 arguments, but got 0 or more.
96
120
  super(...args)
97
121
 
98
- // @ts-expect-error ts-migrate(2339) FIXME: Property '_labelId' does not exist on type 'Pagina... Remove this comment to see the full error message
99
122
  this._labelId = uid('Pagination')
100
-
101
- // @ts-expect-error ts-migrate(2339) FIXME: Property '_prevButton' does not exist on type 'Pag... Remove this comment to see the full error message
102
- this._prevButton = null
103
- // @ts-expect-error ts-migrate(2339) FIXME: Property '_nextButton' does not exist on type 'Pag... Remove this comment to see the full error message
104
- this._nextButton = null
105
123
  }
124
+
106
125
  get _root() {
107
126
  console.warn(
108
127
  '_root property is deprecated and will be removed in v9, please use ref instead'
@@ -111,40 +130,80 @@ class Pagination extends Component<PaginationProps> {
111
130
  return this.ref
112
131
  }
113
132
 
114
- getSnapshotBeforeUpdate() {
133
+ get inputMode() {
134
+ return this.props.variant === 'input'
135
+ }
136
+
137
+ get childPages() {
138
+ return childrenArray(this.props)
139
+ }
140
+
141
+ get withFirstAndLastButton() {
142
+ return this.inputMode || this.props.withFirstAndLastButton
143
+ }
144
+
145
+ get showDisabledButtons() {
146
+ return this.inputMode || this.props.showDisabledButtons
147
+ }
148
+
149
+ getSnapshotBeforeUpdate(): PaginationSnapshot {
115
150
  const activeElement = getActiveElement()
116
151
 
117
- return {
118
- // @ts-expect-error ts-migrate(2339) FIXME: Property '_prevButton' does not exist on type 'Pag... Remove this comment to see the full error message
119
- prevButtonFocused: this._prevButton === activeElement,
120
- // @ts-expect-error ts-migrate(2339) FIXME: Property '_nextButton' does not exist on type 'Pag... Remove this comment to see the full error message
121
- nextButtonFocused: this._nextButton === activeElement
152
+ if (
153
+ activeElement === this._firstButton ||
154
+ activeElement === this._prevButton ||
155
+ activeElement === this._nextButton ||
156
+ activeElement === this._lastButton
157
+ ) {
158
+ return { lastFocusedButton: activeElement as HTMLButtonElement }
159
+ } else {
160
+ return { lastFocusedButton: undefined }
122
161
  }
123
162
  }
124
163
 
125
164
  componentDidMount() {
126
- // @ts-expect-error ts-migrate(2722) FIXME: Cannot invoke an object which is possibly 'undefin... Remove this comment to see the full error message
127
- this.props.makeStyles()
165
+ this.props.makeStyles?.()
128
166
  }
129
167
 
130
- // @ts-expect-error ts-migrate(7006) FIXME: Parameter 'prevProps' implicitly has an 'any' type... Remove this comment to see the full error message
131
- componentDidUpdate(prevProps, prevState, snapshot) {
132
- // @ts-expect-error ts-migrate(2722) FIXME: Cannot invoke an object which is possibly 'undefin... Remove this comment to see the full error message
133
- this.props.makeStyles()
168
+ componentDidUpdate(
169
+ prevProps: PaginationProps,
170
+ _prevState: unknown,
171
+ snapshot: PaginationSnapshot
172
+ ) {
173
+ this.props.makeStyles?.()
174
+
134
175
  if (
135
176
  !this.props.shouldHandleFocus ||
136
177
  (!propsHaveCompactView(prevProps) && !propsHaveCompactView(this.props))
137
178
  ) {
138
179
  return
180
+ } else {
181
+ this.focusElementAfterUpdate(snapshot)
139
182
  }
183
+ }
140
184
 
141
- const { prevButtonFocused, nextButtonFocused } = snapshot || {}
185
+ focusElementAfterUpdate(snapshot: PaginationSnapshot) {
186
+ const { lastFocusedButton } = snapshot
142
187
 
143
- if (prevButtonFocused || nextButtonFocused) {
188
+ if (lastFocusedButton) {
144
189
  const focusable = findTabbable(this.ref)
145
- const focusIndex = prevButtonFocused ? 0 : focusable.length - 1
146
- // @ts-expect-error ts-migrate(2554) FIXME:
147
- focusable[focusIndex].focus()
190
+ const direction = lastFocusedButton.dataset.direction
191
+
192
+ // By default we want to focus the previously focused button
193
+ let nextFocusElement: Element = lastFocusedButton
194
+
195
+ // In case the button is not focusable anymore
196
+ // (disabled or not in the DOM), we focus to the next available page
197
+ if (!focusable.includes(nextFocusElement)) {
198
+ if (direction === 'first' || direction === 'prev') {
199
+ nextFocusElement = focusable[0]
200
+ }
201
+ if (direction === 'next' || direction === 'last') {
202
+ nextFocusElement = focusable[focusable.length - 1]
203
+ }
204
+ }
205
+
206
+ ;(nextFocusElement as HTMLElement).focus()
148
207
  }
149
208
  }
150
209
 
@@ -152,17 +211,15 @@ class Pagination extends Component<PaginationProps> {
152
211
  return propsHaveCompactView(this.props)
153
212
  }
154
213
 
155
- // @ts-expect-error ts-migrate(7006) FIXME: Parameter 'children' implicitly has an 'any' type.
156
- transferDisabledPropToChildren(children) {
157
- return this.props.disabled
214
+ transferDisabledPropToChildren(children: PaginationProps['children']) {
215
+ return children && this.props.disabled
158
216
  ? React.Children.map(children, (page) =>
159
217
  React.cloneElement(page, { disabled: this.props.disabled })
160
218
  )
161
219
  : children
162
220
  }
163
221
 
164
- // @ts-expect-error ts-migrate(7006) FIXME: Parameter 'el' implicitly has an 'any' type.
165
- handleElementRef = (el) => {
222
+ handleElementRef = (el: Element | null) => {
166
223
  this.ref = el
167
224
  if (el) {
168
225
  if (typeof this.props.elementRef === 'function') {
@@ -171,8 +228,12 @@ class Pagination extends Component<PaginationProps> {
171
228
  }
172
229
  }
173
230
 
231
+ handleInputRef = (el: Element | null) => {
232
+ this._inputRef = el
233
+ }
234
+
174
235
  renderLabel() {
175
- const display = this.props.variant === 'compact' ? 'block' : 'inline-block'
236
+ const display = this.props.variant === 'full' ? 'inline-block' : 'block'
176
237
  const visibleLabel = hasVisibleChildren(this.props.label)
177
238
 
178
239
  return (
@@ -180,7 +241,6 @@ class Pagination extends Component<PaginationProps> {
180
241
  as="span"
181
242
  padding={visibleLabel ? 'small' : '0'}
182
243
  display={visibleLabel ? display : 'auto'}
183
- // @ts-expect-error ts-migrate(2339) FIXME: Property '_labelId' does not exist on type 'Pagina... Remove this comment to see the full error message
184
244
  id={this._labelId}
185
245
  >
186
246
  {this.props.label}
@@ -188,14 +248,36 @@ class Pagination extends Component<PaginationProps> {
188
248
  )
189
249
  }
190
250
 
191
- // @ts-expect-error ts-migrate(7006) FIXME: Parameter 'currentPageIndex' implicitly has an 'an... Remove this comment to see the full error message
192
- renderPages(currentPageIndex) {
193
- const allPages = this.props.children
251
+ renderPageInput(currentPageIndex: number) {
252
+ return (
253
+ <PaginationPageInput
254
+ numberOfPages={this.childPages.length}
255
+ currentPageIndex={currentPageIndex}
256
+ onChange={this.handleInputChange.bind(this)}
257
+ screenReaderLabel={this.props.screenReaderLabelNumberInput!}
258
+ label={this.props.labelNumberInput}
259
+ disabled={this.props.disabled}
260
+ inputRef={this.handleInputRef}
261
+ />
262
+ )
263
+ }
264
+
265
+ handleInputChange(
266
+ event:
267
+ | React.KeyboardEvent<HTMLInputElement>
268
+ | React.MouseEvent<HTMLButtonElement>
269
+ | React.FocusEvent<HTMLInputElement>,
270
+ pageIndex: number
271
+ ) {
272
+ this.childPages[pageIndex].props.onClick?.(event)
273
+ }
274
+
275
+ renderPages(currentPageIndex: number) {
276
+ const allPages = this.childPages
194
277
  let visiblePages = allPages
195
278
 
196
279
  if (this.compactView) {
197
280
  const firstIndex = 0
198
- // @ts-expect-error ts-migrate(2533) FIXME: Object is possibly 'null' or 'undefined'.
199
281
  const lastIndex = allPages.length - 1
200
282
 
201
283
  const sliceStart = Math.min(
@@ -204,31 +286,24 @@ class Pagination extends Component<PaginationProps> {
204
286
  )
205
287
  const sliceEnd = Math.min(currentPageIndex + 4, lastIndex)
206
288
 
207
- // @ts-expect-error ts-migrate(2533) FIXME: Object is possibly 'null' or 'undefined'.
208
289
  visiblePages = allPages.slice(sliceStart, sliceEnd)
209
290
 
210
- // @ts-expect-error ts-migrate(2533) FIXME: Object is possibly 'null' or 'undefined'.
211
291
  const firstPage = allPages[firstIndex]
212
- // @ts-expect-error ts-migrate(2533) FIXME: Object is possibly 'null' or 'undefined'.
213
292
  const lastPage = allPages[lastIndex]
214
293
 
215
294
  if (sliceStart - firstIndex > 1)
216
- // @ts-expect-error ts-migrate(2533) FIXME: Object is possibly 'null' or 'undefined'.
217
295
  visiblePages.unshift(
218
296
  <span key="first" aria-hidden="true">
219
297
  &hellip;
220
298
  </span>
221
299
  )
222
- // @ts-expect-error ts-migrate(2533) FIXME: Object is possibly 'null' or 'undefined'.
223
300
  if (sliceStart - firstIndex > 0) visiblePages.unshift(firstPage)
224
301
  if (lastIndex - sliceEnd + 1 > 1)
225
- // @ts-expect-error ts-migrate(2533) FIXME: Object is possibly 'null' or 'undefined'.
226
302
  visiblePages.push(
227
303
  <span key="last" aria-hidden="true">
228
304
  &hellip;
229
305
  </span>
230
306
  )
231
- // @ts-expect-error ts-migrate(2533) FIXME: Object is possibly 'null' or 'undefined'.
232
307
  if (lastIndex - sliceEnd + 1 > 0) visiblePages.push(lastPage)
233
308
  }
234
309
 
@@ -239,33 +314,88 @@ class Pagination extends Component<PaginationProps> {
239
314
  )
240
315
  }
241
316
 
242
- // @ts-expect-error ts-migrate(7006) FIXME: Parameter 'label' implicitly has an 'any' type.
243
- renderArrowButton(label, direction, currentPageIndex) {
244
- // eslint-disable-next-line react/prop-types
245
- const { onClick, disabled } =
246
- // @ts-expect-error ts-migrate(2533) FIXME: Object is possibly 'null' or 'undefined'.
247
- this.props.children[currentPageIndex + direction].props
248
-
249
- // @ts-expect-error ts-migrate(7006) FIXME: Parameter 'el' implicitly has an 'any' type.
250
- const handleButtonRef = (el) => {
251
- if (direction < 0) {
252
- // @ts-expect-error ts-migrate(2339) FIXME: Property '_prevButton' does not exist on type 'Pag... Remove this comment to see the full error message
253
- this._prevButton = el
254
- } else {
255
- // @ts-expect-error ts-migrate(2339) FIXME: Property '_nextButton' does not exist on type 'Pag... Remove this comment to see the full error message
256
- this._nextButton = el
257
- }
317
+ getArrowVariant(
318
+ direction: PaginationArrowDirections,
319
+ currentPageIndex: number,
320
+ pagesCount: number
321
+ ): ArrowConfig {
322
+ switch (direction) {
323
+ case 'first':
324
+ return {
325
+ pageIndex: 0,
326
+ label: this.props.labelFirst || 'First Page',
327
+ shouldEnableIcon: currentPageIndex > 1,
328
+ handleButtonRef: (el) => {
329
+ this._firstButton = el
330
+ }
331
+ }
332
+ case 'prev':
333
+ return {
334
+ pageIndex: currentPageIndex - 1,
335
+ label: this.props.labelPrev || 'Previous Page',
336
+ shouldEnableIcon: currentPageIndex > 0,
337
+ handleButtonRef: (el) => {
338
+ this._prevButton = el
339
+ }
340
+ }
341
+ case 'next':
342
+ return {
343
+ pageIndex: currentPageIndex + 1,
344
+ label: this.props.labelNext || 'Next Page',
345
+ shouldEnableIcon: currentPageIndex < pagesCount - 1,
346
+ handleButtonRef: (el) => {
347
+ this._nextButton = el
348
+ }
349
+ }
350
+ case 'last':
351
+ return {
352
+ pageIndex: pagesCount - 1,
353
+ label: this.props.labelLast || 'Last Page',
354
+ shouldEnableIcon: currentPageIndex < pagesCount - 2,
355
+ handleButtonRef: (el) => {
356
+ this._lastButton = el
357
+ }
358
+ }
258
359
  }
360
+ }
259
361
 
260
- return (
362
+ renderArrowButton(
363
+ direction: PaginationArrowDirections,
364
+ currentPageIndex: number
365
+ ) {
366
+ const { childPages } = this
367
+
368
+ // We don't display the arrows in "compact" variant under 6 items
369
+ if (!(propsHaveCompactView(this.props) || this.inputMode)) {
370
+ return null
371
+ }
372
+
373
+ if (
374
+ !this.withFirstAndLastButton &&
375
+ (direction === 'first' || direction === 'last')
376
+ ) {
377
+ return null
378
+ }
379
+
380
+ const { pageIndex, label, shouldEnableIcon, handleButtonRef } =
381
+ this.getArrowVariant(direction, currentPageIndex, childPages.length)
382
+
383
+ const page = childPages[pageIndex]
384
+
385
+ const disabled =
386
+ page?.props?.disabled || this.props.disabled || !shouldEnableIcon
387
+ const onClick = page?.props?.onClick
388
+
389
+ return shouldEnableIcon || this.showDisabledButtons ? (
261
390
  <PaginationArrowButton
262
- direction={direction === -1 ? 'prev' : 'next'}
391
+ direction={direction}
392
+ data-direction={direction}
263
393
  label={label}
264
394
  onClick={onClick}
265
395
  disabled={disabled}
266
396
  buttonRef={handleButtonRef}
267
397
  />
268
- )
398
+ ) : null
269
399
  }
270
400
 
271
401
  render() {
@@ -290,16 +420,19 @@ class Pagination extends Component<PaginationProps> {
290
420
  elementRef={this.handleElementRef}
291
421
  margin={this.props.margin}
292
422
  css={this.props.styles?.pagination}
293
- // @ts-expect-error ts-migrate(2339) FIXME: Property '_labelId' does not exist on type 'Pagina... Remove this comment to see the full error message
294
- aria-labelledby={this.props.label && this._labelId}
423
+ aria-labelledby={this.props.label ? this._labelId : undefined}
295
424
  >
296
425
  {this.props.label && this.renderLabel()}
297
426
  <View display="inline-block" css={this.props.styles?.pages}>
298
- {shouldShowPrevButton(this.props, currentPageIndex) &&
299
- this.renderArrowButton(this.props.labelPrev, -1, currentPageIndex)}
300
- {this.renderPages(currentPageIndex)}
301
- {shouldShowNextButton(this.props, currentPageIndex) &&
302
- this.renderArrowButton(this.props.labelNext, 1, currentPageIndex)}
427
+ {this.renderArrowButton('first', currentPageIndex)}
428
+ {this.renderArrowButton('prev', currentPageIndex)}
429
+
430
+ {this.inputMode
431
+ ? this.renderPageInput(currentPageIndex)
432
+ : this.renderPages(currentPageIndex)}
433
+
434
+ {this.renderArrowButton('next', currentPageIndex)}
435
+ {this.renderArrowButton('last', currentPageIndex)}
303
436
  </View>
304
437
  </View>
305
438
  )
@@ -40,14 +40,29 @@ import type {
40
40
  OtherHTMLAttributes,
41
41
  PropValidators
42
42
  } from '@instructure/shared-types'
43
+ import type { PaginationPageProps } from './PaginationButton/props'
43
44
 
44
45
  type PaginationOwnProps = {
45
- children?: React.ReactNode // TODO: oneof([PaginationButton])
46
+ // this won't filter the children completely well,
47
+ // it will allow simple elements (e.g. `<div>`) and components with compatible props
48
+ // TODO: find a better way to type Children.oneOf([PaginationButton])
49
+ children?:
50
+ | React.ReactElement<PaginationPageProps>
51
+ | React.ReactElement<PaginationPageProps>[]
46
52
  disabled?: boolean
53
+ withFirstAndLastButton?: boolean
54
+ showDisabledButtons?: boolean
47
55
  label?: React.ReactNode
48
56
  labelNext?: string
49
57
  labelPrev?: string
50
- variant?: 'full' | 'compact'
58
+ labelFirst?: string
59
+ labelLast?: string
60
+ labelNumberInput?: (numberOfPages: number) => React.ReactNode
61
+ screenReaderLabelNumberInput?: (
62
+ currentPage: number,
63
+ numberOfPages: number
64
+ ) => string
65
+ variant?: 'full' | 'compact' | 'input'
51
66
  margin?: Spacing
52
67
  as?: AsElementType
53
68
  elementRef?: (element: Element | null) => void
@@ -73,6 +88,14 @@ const propTypes: PropValidators<PropKeys> = {
73
88
  * Disables interaction with all pages
74
89
  */
75
90
  disabled: PropTypes.bool,
91
+ /**
92
+ * Displays "jump to first" and "jump to last" buttons. Always turned on with `input` variant.
93
+ */
94
+ withFirstAndLastButton: PropTypes.bool,
95
+ /**
96
+ * Displays the unavailable navigation buttons as disabled instead of hiding them. Always turned on with `input` variant.
97
+ */
98
+ showDisabledButtons: PropTypes.bool,
76
99
  /**
77
100
  * Visible label for component
78
101
  */
@@ -85,12 +108,28 @@ const propTypes: PropValidators<PropKeys> = {
85
108
  * Accessible label for previous button
86
109
  */
87
110
  labelPrev: PropTypes.string,
111
+ /**
112
+ * Accessible label for "jump to first" button
113
+ */
114
+ labelFirst: PropTypes.string,
115
+ /**
116
+ * Accessible label for "jump to last" button
117
+ */
118
+ labelLast: PropTypes.string,
119
+ /**
120
+ * Label for number input (for `input` variant)
121
+ */
122
+ labelNumberInput: PropTypes.func,
123
+ /**
124
+ * ScreenReaderLabel for number input (for `input` variant)
125
+ */
126
+ screenReaderLabelNumberInput: PropTypes.func,
88
127
  /**
89
128
  * The compact variant truncates the page navigation to show only the first,
90
129
  * last, and pages immediately surrounding the current page. Fewer than 5 pages,
91
130
  * no next/previous arrow buttons will be shown, and all pages will be listed
92
131
  */
93
- variant: PropTypes.oneOf(['full', 'compact']),
132
+ variant: PropTypes.oneOf(['full', 'compact', 'input']),
94
133
  /**
95
134
  * Valid values are `0`, `none`, `auto`, `xxx-small`, `xx-small`, `x-small`,
96
135
  * `small`, `medium`, `large`, `x-large`, `xx-large`. Apply these values via
@@ -116,9 +155,15 @@ const propTypes: PropValidators<PropKeys> = {
116
155
  const allowedProps: AllowedPropKeys = [
117
156
  'children',
118
157
  'disabled',
158
+ 'withFirstAndLastButton',
159
+ 'showDisabledButtons',
119
160
  'label',
120
161
  'labelNext',
121
162
  'labelPrev',
163
+ 'labelFirst',
164
+ 'labelLast',
165
+ 'labelNumberInput',
166
+ 'screenReaderLabelNumberInput',
122
167
  'variant',
123
168
  'margin',
124
169
  'as',
@@ -126,5 +171,9 @@ const allowedProps: AllowedPropKeys = [
126
171
  'shouldHandleFocus'
127
172
  ]
128
173
 
129
- export type { PaginationProps, PaginationStyle }
174
+ type PaginationSnapshot = {
175
+ lastFocusedButton?: HTMLButtonElement
176
+ }
177
+
178
+ export type { PaginationProps, PaginationStyle, PaginationSnapshot }
130
179
  export { propTypes, allowedProps }
@@ -1,7 +1,31 @@
1
1
  {
2
2
  "extends": "../../tsconfig.build.json",
3
3
  "compilerOptions": {
4
- "outDir": "./types"
4
+ "outDir": "./types",
5
+ "rootDir": "./src",
6
+ "composite": true
5
7
  },
6
- "include": ["src/**/*"]
8
+ "include": ["src"],
9
+ "references": [
10
+ { "path": "../ui-babel-preset/tsconfig.build.json" },
11
+ { "path": "../ui-test-locator/tsconfig.build.json" },
12
+ { "path": "../ui-test-queries/tsconfig.build.json" },
13
+ { "path": "../ui-test-utils/tsconfig.build.json" },
14
+ { "path": "../emotion/tsconfig.build.json" },
15
+ { "path": "../shared-types/tsconfig.build.json" },
16
+ { "path": "../ui-a11y-content/tsconfig.build.json" },
17
+ { "path": "../ui-a11y-utils/tsconfig.build.json" },
18
+ { "path": "../ui-buttons/tsconfig.build.json" },
19
+ { "path": "../ui-dom-utils/tsconfig.build.json" },
20
+ { "path": "../ui-icons/tsconfig.build.json" },
21
+ { "path": "../ui-portal/tsconfig.build.json" },
22
+ { "path": "../ui-prop-types/tsconfig.build.json" },
23
+ { "path": "../ui-react-utils/tsconfig.build.json" },
24
+ { "path": "../ui-testable/tsconfig.build.json" },
25
+ { "path": "../ui-tooltip/tsconfig.build.json" },
26
+ { "path": "../ui-utils/tsconfig.build.json" },
27
+ { "path": "../ui-view/tsconfig.build.json" },
28
+ { "path": "../uid/tsconfig.build.json" },
29
+ { "path": "../ui-number-input/tsconfig.build.json" }
30
+ ]
7
31
  }