Skip to content

Commit 4286c83

Browse files
authored
Fix skeleton layout for Search (#4537)
1 parent e320cc3 commit 4286c83

File tree

3 files changed

+396
-15
lines changed

3 files changed

+396
-15
lines changed
Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
import * as React from 'react'
2+
import { render } from '@testing-library/react'
3+
import { MemoryRouter } from 'react-router-dom'
4+
5+
import Results from './Results'
6+
7+
jest.mock('@material-ui/core', () => ({
8+
Button: ({ children }: React.PropsWithChildren<{}>) => <button>{children}</button>,
9+
Icon: ({ children }: React.PropsWithChildren<{}>) => <span>{children}</span>,
10+
makeStyles: () => () => ({}),
11+
useTheme: () => ({
12+
breakpoints: { down: () => false },
13+
spacing: (x: number) => x * 8,
14+
}),
15+
useMediaQuery: () => false,
16+
}))
17+
18+
jest.mock('@material-ui/icons', () => ({
19+
GridOn: () => 'table icon',
20+
List: () => 'list icon',
21+
}))
22+
23+
jest.mock('@material-ui/lab', () => ({
24+
Skeleton: () => <div>Loading…</div>,
25+
ToggleButtonGroup: ({
26+
value,
27+
children,
28+
}: React.PropsWithChildren<{ value: string }>) => (
29+
<ul data-selected={value}>{children}</ul>
30+
),
31+
ToggleButton: ({ children, value }: React.PropsWithChildren<{ value: string }>) => (
32+
<li data-value={value}>{children}</li>
33+
),
34+
}))
35+
36+
const model = {
37+
state: {
38+
resultType: 'p', // QuiltPackage
39+
view: 'l', // List
40+
searchString: 'test',
41+
buckets: ['test-bucket'],
42+
order: 'BEST_MATCH',
43+
filter: {
44+
predicates: {},
45+
order: [],
46+
},
47+
userMetaFilters: {
48+
filters: new Map(),
49+
},
50+
latestOnly: true,
51+
},
52+
actions: {
53+
setView: jest.fn(),
54+
setOrder: jest.fn(),
55+
},
56+
firstPageQuery: {
57+
_tag: 'fetching',
58+
} as any,
59+
baseSearchQuery: {
60+
_tag: 'fetching',
61+
} as any,
62+
}
63+
64+
jest.mock('../model', () => ({
65+
use: () => model,
66+
ResultType: {
67+
QuiltPackage: 'p',
68+
S3Object: 'o',
69+
},
70+
View: {
71+
Table: 't',
72+
List: 'l',
73+
},
74+
}))
75+
76+
jest.mock('containers/Bucket/PackageDialog/PackageCreationForm', () => ({
77+
usePackageCreationDialog: () => ({
78+
open: jest.fn(),
79+
render: () => <>Don't forget to render dialog</>,
80+
}),
81+
}))
82+
83+
jest.mock('containers/Bucket/Routes', () => ({
84+
useBucketStrict: () => 'test-bucket',
85+
}))
86+
87+
jest.mock('./ColumnTitle', () => ({ children }: React.PropsWithChildren<{}>) => (
88+
<div>Column Title: {children}</div>
89+
))
90+
91+
jest.mock('../Sort', () => () => <div>Sort Selector</div>)
92+
93+
jest.mock('utils/NamedRoutes', () => ({
94+
use: () => ({
95+
paths: {
96+
bucketRoot: '/b/:bucket',
97+
},
98+
}),
99+
}))
100+
101+
describe('containers/Search/Layout/Results', () => {
102+
beforeEach(() => {
103+
jest.clearAllMocks()
104+
model.firstPageQuery = { _tag: 'fetching' }
105+
model.state.resultType = 'p'
106+
})
107+
108+
it('renders with loading state', () => {
109+
const { container } = render(
110+
<MemoryRouter>
111+
<Results />
112+
</MemoryRouter>,
113+
)
114+
expect(container).toMatchSnapshot()
115+
})
116+
117+
it('renders with data and shows number of results', () => {
118+
model.firstPageQuery = {
119+
_tag: 'data',
120+
data: {
121+
__typename: 'PackagesSearchResultSet',
122+
total: 5,
123+
},
124+
}
125+
126+
const { container } = render(
127+
<MemoryRouter>
128+
<Results />
129+
</MemoryRouter>,
130+
)
131+
expect(container).toMatchSnapshot()
132+
})
133+
134+
it('renders with FiltersButton when onFilters prop is provided', () => {
135+
model.firstPageQuery = {
136+
_tag: 'data',
137+
data: {
138+
__typename: 'PackagesSearchResultSet',
139+
total: 3,
140+
},
141+
}
142+
143+
const { container } = render(
144+
<MemoryRouter>
145+
<Results onFilters={jest.fn()} />
146+
</MemoryRouter>,
147+
)
148+
expect(container).toMatchSnapshot()
149+
})
150+
151+
it('does not show ToggleResultsView for S3Object result type', () => {
152+
model.state.resultType = 'o' // S3Object
153+
model.firstPageQuery = {
154+
_tag: 'data',
155+
data: {
156+
__typename: 'ObjectsSearchResultSet',
157+
total: 5,
158+
},
159+
}
160+
161+
const { container } = render(
162+
<MemoryRouter>
163+
<Results />
164+
</MemoryRouter>,
165+
)
166+
expect(container).toMatchSnapshot()
167+
})
168+
169+
it('renders error state', () => {
170+
model.firstPageQuery = {
171+
_tag: 'error',
172+
error: new Error('Test error'),
173+
}
174+
175+
const { container } = render(
176+
<MemoryRouter>
177+
<Results />
178+
</MemoryRouter>,
179+
)
180+
expect(container).toMatchSnapshot()
181+
})
182+
183+
it('shows Create Package button in bucket', () => {
184+
model.firstPageQuery = {
185+
_tag: 'data',
186+
data: {
187+
__typename: 'PackagesSearchResultSet',
188+
total: 5,
189+
},
190+
}
191+
const { container } = render(
192+
<MemoryRouter initialEntries={['/b/test-bucket/packages/my-package']}>
193+
<Results />
194+
</MemoryRouter>,
195+
)
196+
expect(container).toMatchSnapshot()
197+
})
198+
})

catalog/app/containers/Search/Layout/Results.tsx

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ import cx from 'classnames'
22
import * as React from 'react'
33
import * as RRDom from 'react-router-dom'
44
import * as M from '@material-ui/core'
5+
import { GridOn as IconGridOn, List as IconList } from '@material-ui/icons'
56
import * as Lab from '@material-ui/lab'
67

7-
import Skeleton from 'components/Skeleton'
88
import { usePackageCreationDialog } from 'containers/Bucket/PackageDialog/PackageCreationForm'
99
import { useBucketStrict } from 'containers/Bucket/Routes'
1010
import * as NamedRoutes from 'utils/NamedRoutes'
@@ -97,16 +97,12 @@ function resultsCountI18n(n: number, state: SearchUIModel.SearchUrlState) {
9797
return Format.pluralify(n, I18_COUNT_RESULTS)
9898
}
9999

100-
interface ResultsCountProps {
101-
className: string
102-
}
103-
104-
function ResultsCount({ className }: ResultsCountProps) {
100+
function ResultsCount() {
105101
const model = SearchUIModel.use()
106102
const r = model.firstPageQuery
107103
switch (r._tag) {
108104
case 'fetching':
109-
return <Skeleton width={140} height={24} />
105+
return <Lab.Skeleton width={140} />
110106
case 'error':
111107
return null
112108
case 'data':
@@ -117,11 +113,7 @@ function ResultsCount({ className }: ResultsCountProps) {
117113
return null
118114
case 'ObjectsSearchResultSet':
119115
case 'PackagesSearchResultSet':
120-
return (
121-
<ColumnTitle className={className}>
122-
{resultsCountI18n(r.data.total, model.state)}
123-
</ColumnTitle>
124-
)
116+
return <>{resultsCountI18n(r.data.total, model.state)}</>
125117
default:
126118
assertNever(r.data)
127119
}
@@ -187,10 +179,10 @@ function ToggleResultsView({ className }: ToggleResultsViewProps) {
187179
size="small"
188180
>
189181
<Lab.ToggleButton value={SearchUIModel.View.Table} classes={classes}>
190-
<M.Icon>grid_on</M.Icon>
182+
<IconGridOn />
191183
</Lab.ToggleButton>
192184
<Lab.ToggleButton value={SearchUIModel.View.List} classes={classes}>
193-
<M.Icon>list</M.Icon>
185+
<IconList />
194186
</Lab.ToggleButton>
195187
</Lab.ToggleButtonGroup>
196188
)
@@ -244,7 +236,9 @@ export default function Results({ onFilters }: ResultsProps) {
244236
const { paths } = NamedRoutes.use()
245237
return (
246238
<div className={classes.root}>
247-
<ResultsCount className={classes.title} />
239+
<ColumnTitle className={classes.title}>
240+
<ResultsCount />
241+
</ColumnTitle>
248242

249243
<div className={classes.controls}>
250244
<RRDom.Switch>

0 commit comments

Comments
 (0)