Skip to content

Commit 530848f

Browse files
Luke BowermanElliot Parkgithub-actions[bot]ElliotTheHuman
authored
feat(Pagination): and PageSize support alwaysVisible - visible even when there are no pages to paginate (#2273)
* feat(Pagination): & PageSize now support alwaysVisible Automatically keeps pagination visible even when there are no pages to paginate * Minor updates to Pagination test text * PageSize - if given a per page value > total, the value in the select === total * chore: image-snapshot updates (#2275) Co-authored-by: ElliotTheHuman <[email protected]> * Added auto-disable behavior when all options > total * Update snapshot Co-authored-by: Elliot Park <[email protected]> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: ElliotTheHuman <[email protected]>
1 parent 5d50fc0 commit 530848f

File tree

7 files changed

+258
-136
lines changed

7 files changed

+258
-136
lines changed
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
3+
MIT License
4+
5+
Copyright (c) 2021 Looker Data Sciences, Inc.
6+
7+
Permission is hereby granted, free of charge, to any person obtaining a copy
8+
of this software and associated documentation files (the "Software"), to deal
9+
in the Software without restriction, including without limitation the rights
10+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
copies of the Software, and to permit persons to whom the Software is
12+
furnished to do so, subject to the following conditions:
13+
14+
The above copyright notice and this permission notice shall be included in all
15+
copies or substantial portions of the Software.
16+
17+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23+
SOFTWARE.
24+
25+
*/
26+
27+
import React, { useState } from 'react'
28+
import { Story } from '@storybook/react/types-6-0'
29+
import { PageSizeProps, PageSize } from '.'
30+
31+
const Template: Story<PageSizeProps> = (args) => <PageSize {...args} />
32+
33+
export const Basic = Template.bind({})
34+
Basic.args = {
35+
onChange: (value: number) => alert(`You chose ${value} per page`),
36+
total: 100,
37+
value: 100,
38+
}
39+
40+
export const AlwaysVisible = () => {
41+
const [value, setValue] = useState(100)
42+
return <PageSize alwaysVisible total={3} value={value} onChange={setValue} />
43+
}
44+
45+
export default {
46+
component: PageSize,
47+
title: 'PageSize',
48+
}

packages/components/src/PageSize/PageSize.test.tsx

Lines changed: 78 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -35,55 +35,82 @@ afterEach(() => {
3535
onChange.mockClear()
3636
})
3737

38-
test('<PageSize/> with defaults', () => {
39-
renderWithTheme(<PageSize value={10} total={1000} onChange={onChange} />)
40-
41-
const select = screen.getByDisplayValue('10')
42-
43-
fireEvent.click(select)
44-
45-
expect(screen.getByText('10')).toBeInTheDocument()
46-
expect(screen.getByText('25')).toBeInTheDocument()
47-
expect(screen.getByText('50')).toBeInTheDocument()
48-
expect(screen.getByText('100')).toBeInTheDocument()
49-
50-
fireEvent.click(screen.getByText('50'))
51-
expect(onChange).toHaveBeenCalledTimes(1)
52-
})
53-
54-
test('<PageSize/> with custom options prop', () => {
55-
renderWithTheme(
56-
<PageSize
57-
value={20}
58-
total={1000}
59-
options={[20, 40, 60]}
60-
onChange={onChange}
61-
/>
62-
)
63-
64-
const select = screen.getByDisplayValue('20')
65-
66-
fireEvent.click(select)
67-
68-
expect(screen.getByText('20')).toBeInTheDocument()
69-
expect(screen.getByText('40')).toBeInTheDocument()
70-
expect(screen.getByText('60')).toBeInTheDocument()
71-
72-
fireEvent.click(screen.getByText('40'))
73-
expect(onChange).toHaveBeenCalledTimes(1)
74-
})
75-
76-
test('<PageSize/> does not load when smallest option >= total', () => {
77-
renderWithTheme(
78-
<PageSize
79-
value={100}
80-
total={5}
81-
options={[100, 200, 300]}
82-
onChange={onChange}
83-
/>
84-
)
85-
86-
expect(screen.queryByText('Display')).not.toBeInTheDocument()
87-
expect(screen.queryByDisplayValue('100')).not.toBeInTheDocument()
88-
expect(screen.queryByText('of 5')).not.toBeInTheDocument()
38+
describe('PageSize', () => {
39+
test('defaults', () => {
40+
renderWithTheme(<PageSize value={10} total={1000} onChange={onChange} />)
41+
42+
const select = screen.getByDisplayValue('10')
43+
expect(screen.getByText('of 1000')).toBeInTheDocument()
44+
45+
fireEvent.click(select)
46+
47+
expect(screen.getByText('10')).toBeInTheDocument()
48+
expect(screen.getByText('25')).toBeInTheDocument()
49+
expect(screen.getByText('50')).toBeInTheDocument()
50+
expect(screen.getByText('100')).toBeInTheDocument()
51+
52+
fireEvent.click(screen.getByText('50'))
53+
expect(onChange).toHaveBeenCalledTimes(1)
54+
})
55+
56+
test('custom options prop', () => {
57+
renderWithTheme(
58+
<PageSize
59+
value={20}
60+
total={1000}
61+
options={[20, 40, 60]}
62+
onChange={onChange}
63+
/>
64+
)
65+
66+
const select = screen.getByDisplayValue('20')
67+
expect(screen.getByText('of 1000')).toBeInTheDocument()
68+
69+
fireEvent.click(select)
70+
71+
expect(screen.getByText('20')).toBeInTheDocument()
72+
expect(screen.getByText('40')).toBeInTheDocument()
73+
expect(screen.getByText('60')).toBeInTheDocument()
74+
75+
fireEvent.click(screen.getByText('40'))
76+
expect(onChange).toHaveBeenCalledTimes(1)
77+
})
78+
79+
test('does not load when smallest option >= total', () => {
80+
renderWithTheme(
81+
<PageSize
82+
value={100}
83+
total={5}
84+
options={[100, 200, 300]}
85+
onChange={jest.fn()}
86+
/>
87+
)
88+
89+
expect(screen.queryByText('Display')).not.toBeInTheDocument()
90+
expect(screen.queryByDisplayValue('100')).not.toBeInTheDocument()
91+
expect(screen.queryByText('of 5')).not.toBeInTheDocument()
92+
})
93+
94+
test('alwaysVisible', () => {
95+
renderWithTheme(
96+
<PageSize
97+
value={100}
98+
total={5}
99+
options={[100, 200, 300]}
100+
onChange={jest.fn()}
101+
alwaysVisible
102+
/>
103+
)
104+
105+
const select = screen.getByDisplayValue('5')
106+
expect(select).toBeInTheDocument()
107+
expect(screen.getByText('of 5')).toBeInTheDocument()
108+
109+
// This select should be auto-disabled since all options are greater than the total
110+
fireEvent.click(select)
111+
112+
expect(screen.queryByText('100')).not.toBeInTheDocument()
113+
expect(screen.queryByText('200')).not.toBeInTheDocument()
114+
expect(screen.queryByText('300')).not.toBeInTheDocument()
115+
})
89116
})

packages/components/src/PageSize/PageSize.tsx

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,12 @@ export interface PageSizeProps {
4848
* Callback that triggers when new value is selected
4949
*/
5050
onChange: (value: number) => void
51+
/**
52+
* If enabled controls will always be shown regardless of whether or not
53+
* there are any additional pages to be displayed.
54+
* @default false
55+
*/
56+
alwaysVisible?: boolean
5157
}
5258

5359
const defaultPageSizes = [10, 25, 50, 100]
@@ -61,6 +67,7 @@ const arrayToSelectOptions = (options: Array<string | number>) =>
6167
options.map((option) => stringToSelectOption(String(option)))
6268

6369
export const PageSizeLayout: FC<PageSizeProps> = ({
70+
alwaysVisible = false,
6471
value,
6572
total,
6673
className,
@@ -71,17 +78,20 @@ export const PageSizeLayout: FC<PageSizeProps> = ({
7178

7279
const handleOnChange = (newValue: string) => onChange(Number(newValue))
7380

74-
return Math.min(...options) < total ? (
81+
return alwaysVisible || Math.min(...options) < total ? (
7582
<div className={className}>
7683
{t('Display')}
7784
<Select
7885
width="5rem"
7986
mx="xsmall"
8087
options={arrayToSelectOptions(options)}
81-
value={String(value)}
88+
value={String(value > total ? total : value)}
8289
onChange={handleOnChange}
90+
disabled={options.every((option) => option > total)}
8391
/>
84-
{t('of')} {total}
92+
<span>
93+
{t('of')} {total}
94+
</span>
8595
</div>
8696
) : null
8797
}

0 commit comments

Comments
 (0)