-
-
Notifications
You must be signed in to change notification settings - Fork 5.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Chore(ArrayField): add support for storeKey
to manage independent selection states
#10390
base: next
Are you sure you want to change the base?
Changes from all commits
67ac0a8
125477e
a3e5c31
d852f0c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
import * as React from 'react'; | ||
import { | ||
render, | ||
fireEvent, | ||
screen, | ||
waitFor, | ||
act, | ||
} from '@testing-library/react'; | ||
import { | ||
ListsWithoutStoreKeys, | ||
ListsWithStoreKeys, | ||
} from './useList.storekey.stories'; | ||
import { TestMemoryRouter } from '../../routing'; | ||
|
||
beforeEach(() => { | ||
// Clear localStorage or mock store to reset state | ||
localStorage.clear(); | ||
}); | ||
|
||
describe('useList', () => { | ||
describe('storeKey', () => { | ||
it('should keep distinct two lists of the same resource given different keys', async () => { | ||
render( | ||
<TestMemoryRouter initialEntries={['/top']}> | ||
<ListsWithStoreKeys /> | ||
</TestMemoryRouter> | ||
); | ||
|
||
// Wait for the initial state of perPage to stabilize | ||
await waitFor(() => { | ||
const perPageValue = screen | ||
.getByLabelText('perPage') | ||
.getAttribute('data-value'); | ||
expect(perPageValue).toEqual('3'); | ||
}); | ||
|
||
act(() => { | ||
fireEvent.click(screen.getByLabelText('incrementPerPage')); | ||
}); | ||
|
||
await waitFor(() => { | ||
const perPageValue = screen | ||
.getByLabelText('perPage') | ||
.getAttribute('data-value'); | ||
expect(perPageValue).toEqual('4'); | ||
}); | ||
|
||
// Navigate to "flop" list | ||
act(() => { | ||
fireEvent.click(screen.getByLabelText('flop')); | ||
}); | ||
|
||
await waitFor(() => { | ||
const perPageValue = screen | ||
.getByLabelText('perPage') | ||
.getAttribute('data-value'); | ||
expect(perPageValue).toEqual('3'); | ||
}); | ||
}); | ||
|
||
it('should not use the store when storeKey is false', async () => { | ||
render( | ||
<TestMemoryRouter initialEntries={['/store']}> | ||
<ListsWithoutStoreKeys /> | ||
</TestMemoryRouter> | ||
); | ||
|
||
await waitFor(() => { | ||
expect( | ||
screen.getByLabelText('perPage').getAttribute('data-value') | ||
).toEqual('3'); | ||
}); | ||
|
||
act(() => { | ||
fireEvent.click(screen.getByLabelText('incrementPerPage')); | ||
fireEvent.click(screen.getByLabelText('incrementPerPage')); | ||
}); | ||
|
||
await waitFor(() => { | ||
expect( | ||
screen.getByLabelText('perPage').getAttribute('data-value') | ||
).toEqual('5'); | ||
}); | ||
|
||
act(() => { | ||
fireEvent.click(screen.getByLabelText('nostore')); | ||
}); | ||
|
||
await waitFor(() => { | ||
const storeKey = screen | ||
.getByLabelText('nostore') | ||
.getAttribute('data-value'); | ||
expect(storeKey).toEqual(null); | ||
}); | ||
|
||
expect( | ||
screen.getByLabelText('perPage').getAttribute('data-value') | ||
).toEqual('3'); | ||
|
||
act(() => { | ||
fireEvent.click(screen.getByLabelText('incrementPerPage')); | ||
}); | ||
|
||
await waitFor(() => { | ||
expect( | ||
screen.getByLabelText('perPage').getAttribute('data-value') | ||
).toEqual('4'); | ||
}); | ||
|
||
act(() => { | ||
fireEvent.click(screen.getByLabelText('store')); | ||
}); | ||
// Shouldn't have changed the store list | ||
await waitFor(() => { | ||
const perPageValue = screen | ||
.getByLabelText('perPage') | ||
.getAttribute('data-value'); | ||
expect(perPageValue).toEqual('5'); | ||
}); | ||
|
||
act(() => { | ||
fireEvent.click(screen.getByLabelText('nostore')); | ||
}); | ||
// Should have reset its parameters to their default | ||
expect( | ||
screen.getByLabelText('perPage').getAttribute('data-value') | ||
).toEqual('3'); | ||
}); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
import * as React from 'react'; | ||
import { Route } from 'react-router'; | ||
import { Link } from 'react-router-dom'; | ||
import fakeDataProvider from 'ra-data-fakerest'; | ||
|
||
import { | ||
CoreAdminContext, | ||
CoreAdminUI, | ||
CustomRoutes, | ||
Resource, | ||
} from '../../core'; | ||
import { localStorageStore } from '../../store'; | ||
import { FakeBrowserDecorator } from '../../storybook/FakeBrowser'; | ||
import { CoreLayoutProps, SortPayload } from '../../types'; | ||
import { useList } from './useList'; | ||
|
||
export default { | ||
title: 'ra-core/controller/list/useList', | ||
decorators: [FakeBrowserDecorator], | ||
parameters: { | ||
initialEntries: ['/top'], | ||
}, | ||
}; | ||
|
||
const styles = { | ||
mainContainer: { | ||
margin: '20px 10px', | ||
}, | ||
ul: { | ||
marginTop: '20px', | ||
padding: '10px', | ||
}, | ||
}; | ||
|
||
const dataProvider = fakeDataProvider({ | ||
posts: [ | ||
{ id: 1, title: 'Post #1', votes: 90 }, | ||
{ id: 2, title: 'Post #2', votes: 20 }, | ||
{ id: 3, title: 'Post #3', votes: 30 }, | ||
{ id: 4, title: 'Post #4', votes: 40 }, | ||
{ id: 5, title: 'Post #5', votes: 50 }, | ||
{ id: 6, title: 'Post #6', votes: 60 }, | ||
{ id: 7, title: 'Post #7', votes: 70 }, | ||
], | ||
}); | ||
|
||
const OrderedPostList = ({ | ||
storeKey, | ||
sort, | ||
}: { | ||
storeKey: string | false; | ||
sort?: SortPayload; | ||
}) => { | ||
const params = useList({ | ||
resource: 'posts', | ||
perPage: 3, | ||
sort, | ||
storeKey, | ||
}); | ||
|
||
return ( | ||
<div> | ||
<span aria-label="storeKey" data-value={storeKey}> | ||
storeKey: {storeKey} | ||
</span> | ||
<br /> | ||
<span aria-label="perPage" data-value={params.perPage}> | ||
perPage: {params.perPage} | ||
</span> | ||
<br /> | ||
<button | ||
aria-label="incrementPerPage" | ||
onClick={() => { | ||
return params.setPerPage(++params.perPage); | ||
}} | ||
> | ||
Increment perPage | ||
</button> | ||
<ul style={styles.ul}> | ||
{params.data?.map(post => ( | ||
<li key={post.id}> | ||
{post.title} - {post.votes} votes | ||
</li> | ||
))} | ||
</ul> | ||
</div> | ||
); | ||
}; | ||
|
||
const Layout = (props: CoreLayoutProps) => ( | ||
<div style={styles.mainContainer}> | ||
<Link aria-label="top" to={`/top`}> | ||
Go to Top Posts | ||
</Link> | ||
<Link aria-label="flop" to={`/flop`}> | ||
Go to Flop Posts | ||
</Link> | ||
<Link aria-label="store" to={`/store`}> | ||
Go to Store List | ||
</Link> | ||
<Link aria-label="nostore" to={`/nostore`}> | ||
Go to No-Store List | ||
</Link> | ||
|
||
<br /> | ||
{props.children} | ||
</div> | ||
); | ||
Comment on lines
+90
to
+108
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
const TopPosts = ( | ||
<OrderedPostList | ||
storeKey="topPostsKey" | ||
sort={{ field: 'votes', order: 'DESC' }} | ||
/> | ||
); | ||
const FlopPosts = ( | ||
<OrderedPostList | ||
storeKey="flopPostsKey" | ||
sort={{ field: 'votes', order: 'ASC' }} | ||
/> | ||
); | ||
const StorePosts = ( | ||
<OrderedPostList | ||
storeKey="storeListKey" | ||
sort={{ field: 'votes', order: 'ASC' }} | ||
/> | ||
); | ||
const NoStorePosts = ( | ||
<OrderedPostList storeKey={false} sort={{ field: 'votes', order: 'ASC' }} /> | ||
); | ||
|
||
export const ListsWithStoreKeys = () => ( | ||
<CoreAdminContext store={localStorageStore()} dataProvider={dataProvider}> | ||
<CoreAdminUI layout={Layout}> | ||
<CustomRoutes> | ||
<Route path="/top" element={TopPosts} /> | ||
<Route path="/flop" element={FlopPosts} /> | ||
</CustomRoutes> | ||
<Resource name="posts" /> | ||
</CoreAdminUI> | ||
</CoreAdminContext> | ||
); | ||
|
||
export const ListsWithoutStoreKeys = () => ( | ||
<CoreAdminContext store={localStorageStore()} dataProvider={dataProvider}> | ||
<CoreAdminUI layout={Layout}> | ||
<CustomRoutes> | ||
<Route path="/store" element={StorePosts} /> | ||
<Route path="/nostore" element={NoStorePosts} /> | ||
</CustomRoutes> | ||
<Resource name="posts" /> | ||
</CoreAdminUI> | ||
</CoreAdminContext> | ||
); | ||
Comment on lines
+144
to
+154
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This Story is a bit misleading, Besides, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.