Set initial value from queryParameters
Add tests for external queryParameter updates Signed-off-by: Tim Hansen <timbonicus@gmail.com>
This commit is contained in:
+30
@@ -161,4 +161,34 @@ describe('<EntityLifecyclePicker/>', () => {
|
||||
lifecycles: undefined,
|
||||
});
|
||||
});
|
||||
|
||||
it('responds to external queryParameters changes', () => {
|
||||
const updateFilters = jest.fn();
|
||||
const rendered = render(
|
||||
<MockEntityListContextProvider
|
||||
value={{
|
||||
updateFilters,
|
||||
queryParameters: { lifecycles: ['experimental'] },
|
||||
}}
|
||||
>
|
||||
<EntityLifecyclePicker />
|
||||
</MockEntityListContextProvider>,
|
||||
);
|
||||
expect(updateFilters).toHaveBeenLastCalledWith({
|
||||
lifecycles: new EntityLifecycleFilter(['experimental']),
|
||||
});
|
||||
rendered.rerender(
|
||||
<MockEntityListContextProvider
|
||||
value={{
|
||||
updateFilters,
|
||||
queryParameters: { lifecycles: ['production'] },
|
||||
}}
|
||||
>
|
||||
<EntityLifecyclePicker />
|
||||
</MockEntityListContextProvider>,
|
||||
);
|
||||
expect(updateFilters).toHaveBeenLastCalledWith({
|
||||
lifecycles: new EntityLifecycleFilter(['production']),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+12
-6
@@ -51,18 +51,24 @@ export const EntityLifecyclePicker = () => {
|
||||
const { updateFilters, backendEntities, filters, queryParameters } =
|
||||
useEntityListProvider();
|
||||
|
||||
const queryParamLifecycles = useMemo(
|
||||
() => [queryParameters.lifecycles].flat().filter(Boolean) as string[],
|
||||
[queryParameters],
|
||||
);
|
||||
|
||||
const [selectedLifecycles, setSelectedLifecycles] = useState(
|
||||
filters.lifecycles?.values ?? [],
|
||||
queryParamLifecycles.length
|
||||
? queryParamLifecycles
|
||||
: filters.lifecycles?.values ?? [],
|
||||
);
|
||||
|
||||
// Set selected lifecycles on query parameter updates; this happens at initial page load and from
|
||||
// external updates to the page location.
|
||||
useEffect(() => {
|
||||
const queryParamLifecycles = [queryParameters.lifecycles]
|
||||
.flat()
|
||||
.filter(Boolean) as string[];
|
||||
setSelectedLifecycles(queryParamLifecycles);
|
||||
}, [queryParameters]);
|
||||
if (queryParamLifecycles.length) {
|
||||
setSelectedLifecycles(queryParamLifecycles);
|
||||
}
|
||||
}, [queryParamLifecycles]);
|
||||
|
||||
useEffect(() => {
|
||||
updateFilters({
|
||||
|
||||
@@ -191,4 +191,34 @@ describe('<EntityOwnerPicker/>', () => {
|
||||
owner: undefined,
|
||||
});
|
||||
});
|
||||
|
||||
it('responds to external queryParameters changes', () => {
|
||||
const updateFilters = jest.fn();
|
||||
const rendered = render(
|
||||
<MockEntityListContextProvider
|
||||
value={{
|
||||
updateFilters,
|
||||
queryParameters: { owners: ['team-a'] },
|
||||
}}
|
||||
>
|
||||
<EntityOwnerPicker />
|
||||
</MockEntityListContextProvider>,
|
||||
);
|
||||
expect(updateFilters).toHaveBeenLastCalledWith({
|
||||
owners: new EntityOwnerFilter(['team-a']),
|
||||
});
|
||||
rendered.rerender(
|
||||
<MockEntityListContextProvider
|
||||
value={{
|
||||
updateFilters,
|
||||
queryParameters: { owners: ['team-b'] },
|
||||
}}
|
||||
>
|
||||
<EntityOwnerPicker />
|
||||
</MockEntityListContextProvider>,
|
||||
);
|
||||
expect(updateFilters).toHaveBeenLastCalledWith({
|
||||
owners: new EntityOwnerFilter(['team-b']),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -53,18 +53,22 @@ export const EntityOwnerPicker = () => {
|
||||
const { updateFilters, backendEntities, filters, queryParameters } =
|
||||
useEntityListProvider();
|
||||
|
||||
const queryParamOwners = useMemo(
|
||||
() => [queryParameters.owners].flat().filter(Boolean) as string[],
|
||||
[queryParameters],
|
||||
);
|
||||
|
||||
const [selectedOwners, setSelectedOwners] = useState(
|
||||
filters.owners?.values ?? [],
|
||||
queryParamOwners.length ? queryParamOwners : filters.owners?.values ?? [],
|
||||
);
|
||||
|
||||
// Set selected owners on query parameter updates; this happens at initial page load and from
|
||||
// external updates to the page location.
|
||||
useEffect(() => {
|
||||
const queryParamOwners = [queryParameters.owners]
|
||||
.flat()
|
||||
.filter(Boolean) as string[];
|
||||
setSelectedOwners(queryParamOwners);
|
||||
}, [queryParameters]);
|
||||
if (queryParamOwners.length) {
|
||||
setSelectedOwners(queryParamOwners);
|
||||
}
|
||||
}, [queryParamOwners]);
|
||||
|
||||
useEffect(() => {
|
||||
updateFilters({
|
||||
|
||||
@@ -149,4 +149,34 @@ describe('<EntityTagPicker/>', () => {
|
||||
tags: undefined,
|
||||
});
|
||||
});
|
||||
|
||||
it('responds to external queryParameters changes', () => {
|
||||
const updateFilters = jest.fn();
|
||||
const rendered = render(
|
||||
<MockEntityListContextProvider
|
||||
value={{
|
||||
updateFilters,
|
||||
queryParameters: { tags: ['tag1'] },
|
||||
}}
|
||||
>
|
||||
<EntityTagPicker />
|
||||
</MockEntityListContextProvider>,
|
||||
);
|
||||
expect(updateFilters).toHaveBeenLastCalledWith({
|
||||
tags: new EntityTagFilter(['tag1']),
|
||||
});
|
||||
rendered.rerender(
|
||||
<MockEntityListContextProvider
|
||||
value={{
|
||||
updateFilters,
|
||||
queryParameters: { tags: ['tag2'] },
|
||||
}}
|
||||
>
|
||||
<EntityTagPicker />
|
||||
</MockEntityListContextProvider>,
|
||||
);
|
||||
expect(updateFilters).toHaveBeenLastCalledWith({
|
||||
tags: new EntityTagFilter(['tag2']),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -51,16 +51,22 @@ export const EntityTagPicker = () => {
|
||||
const { updateFilters, backendEntities, filters, queryParameters } =
|
||||
useEntityListProvider();
|
||||
|
||||
const [selectedTags, setSelectedTags] = useState(filters.tags?.values ?? []);
|
||||
const queryParamTags = useMemo(
|
||||
() => [queryParameters.tags].flat().filter(Boolean) as string[],
|
||||
[queryParameters],
|
||||
);
|
||||
|
||||
const [selectedTags, setSelectedTags] = useState(
|
||||
queryParamTags.length ? queryParamTags : filters.tags?.values ?? [],
|
||||
);
|
||||
|
||||
// Set selected tags on query parameter updates; this happens at initial page load and from
|
||||
// external updates to the page location.
|
||||
useEffect(() => {
|
||||
const queryParamTags = [queryParameters.tags]
|
||||
.flat()
|
||||
.filter(Boolean) as string[];
|
||||
setSelectedTags(queryParamTags);
|
||||
}, [queryParameters]);
|
||||
if (queryParamTags.length) {
|
||||
setSelectedTags(queryParamTags);
|
||||
}
|
||||
}, [queryParamTags]);
|
||||
|
||||
useEffect(() => {
|
||||
updateFilters({
|
||||
|
||||
@@ -151,4 +151,38 @@ describe('<EntityTypePicker/>', () => {
|
||||
type: new EntityTypeFilter(['tool']),
|
||||
});
|
||||
});
|
||||
|
||||
it('responds to external queryParameters changes', async () => {
|
||||
const updateFilters = jest.fn();
|
||||
const rendered = await renderWithEffects(
|
||||
<ApiProvider apis={apis}>
|
||||
<MockEntityListContextProvider
|
||||
value={{
|
||||
updateFilters,
|
||||
queryParameters: { type: 'service' },
|
||||
}}
|
||||
>
|
||||
<EntityTypePicker />
|
||||
</MockEntityListContextProvider>
|
||||
</ApiProvider>,
|
||||
);
|
||||
expect(updateFilters).toHaveBeenLastCalledWith({
|
||||
type: new EntityTypeFilter(['service']),
|
||||
});
|
||||
rendered.rerender(
|
||||
<ApiProvider apis={apis}>
|
||||
<MockEntityListContextProvider
|
||||
value={{
|
||||
updateFilters,
|
||||
queryParameters: { type: 'tool' },
|
||||
}}
|
||||
>
|
||||
<EntityTypePicker />
|
||||
</MockEntityListContextProvider>
|
||||
</ApiProvider>,
|
||||
);
|
||||
expect(updateFilters).toHaveBeenLastCalledWith({
|
||||
type: new EntityTypeFilter(['tool']),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -253,6 +253,42 @@ describe('<UserListPicker />', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('responds to external queryParameters changes', () => {
|
||||
const updateFilters = jest.fn();
|
||||
const rendered = render(
|
||||
<ApiProvider apis={apis}>
|
||||
<MockEntityListContextProvider
|
||||
value={{
|
||||
backendEntities,
|
||||
updateFilters,
|
||||
queryParameters: { user: ['all'] },
|
||||
}}
|
||||
>
|
||||
<UserListPicker />
|
||||
</MockEntityListContextProvider>
|
||||
</ApiProvider>,
|
||||
);
|
||||
expect(updateFilters).toHaveBeenLastCalledWith({
|
||||
user: new UserListFilter('all', mockIsOwnedEntity, mockIsStarredEntity),
|
||||
});
|
||||
rendered.rerender(
|
||||
<ApiProvider apis={apis}>
|
||||
<MockEntityListContextProvider
|
||||
value={{
|
||||
backendEntities,
|
||||
updateFilters,
|
||||
queryParameters: { user: ['owned'] },
|
||||
}}
|
||||
>
|
||||
<UserListPicker />
|
||||
</MockEntityListContextProvider>
|
||||
</ApiProvider>,
|
||||
);
|
||||
expect(updateFilters).toHaveBeenLastCalledWith({
|
||||
user: new UserListFilter('owned', mockIsOwnedEntity, mockIsStarredEntity),
|
||||
});
|
||||
});
|
||||
|
||||
describe.each`
|
||||
type | filterFn
|
||||
${'owned'} | ${mockIsOwnedEntity}
|
||||
|
||||
@@ -161,7 +161,14 @@ export const UserListPicker = ({
|
||||
[isOwnedEntity, isStarredEntity],
|
||||
);
|
||||
|
||||
const [selectedUserFilter, setSelectedUserFilter] = useState(initialFilter);
|
||||
const queryParamUserFilter = useMemo(
|
||||
() => [queryParameters.user].flat()[0],
|
||||
[queryParameters],
|
||||
);
|
||||
|
||||
const [selectedUserFilter, setSelectedUserFilter] = useState(
|
||||
queryParamUserFilter ?? initialFilter,
|
||||
);
|
||||
|
||||
// To show proper counts for each section, apply all other frontend filters _except_ the user
|
||||
// filter that's controlled by this picker.
|
||||
@@ -191,9 +198,10 @@ export const UserListPicker = ({
|
||||
// Set selected user filter on query parameter updates; this happens at initial page load and from
|
||||
// external updates to the page location.
|
||||
useEffect(() => {
|
||||
const queryParamUserFilter = [queryParameters.user].flat()[0];
|
||||
setSelectedUserFilter(queryParamUserFilter as UserListFilterKind);
|
||||
}, [queryParameters]);
|
||||
if (queryParamUserFilter) {
|
||||
setSelectedUserFilter(queryParamUserFilter as UserListFilterKind);
|
||||
}
|
||||
}, [queryParamUserFilter]);
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
|
||||
@@ -42,18 +42,22 @@ export function useEntityTypeFilter(): EntityTypeReturn {
|
||||
updateFilters,
|
||||
} = useEntityListProvider();
|
||||
|
||||
const queryParamTypes = useMemo(
|
||||
() => [queryParameters.type].flat().filter(Boolean) as string[],
|
||||
[queryParameters],
|
||||
);
|
||||
|
||||
const [selectedTypes, setSelectedTypes] = useState(
|
||||
typeFilter?.getTypes() ?? [],
|
||||
queryParamTypes.length ? queryParamTypes : typeFilter?.getTypes() ?? [],
|
||||
);
|
||||
|
||||
// Set selected types on query parameter updates; this happens at initial page load and from
|
||||
// external updates to the page location.
|
||||
useEffect(() => {
|
||||
const queryParamTypes = [queryParameters.type]
|
||||
.flat()
|
||||
.filter(Boolean) as string[];
|
||||
setSelectedTypes(queryParamTypes);
|
||||
}, [queryParameters]);
|
||||
if (queryParamTypes.length) {
|
||||
setSelectedTypes(queryParamTypes);
|
||||
}
|
||||
}, [queryParamTypes]);
|
||||
|
||||
const [availableTypes, setAvailableTypes] = useState<string[]>([]);
|
||||
const kind = useMemo(() => kindFilter?.value, [kindFilter]);
|
||||
|
||||
@@ -14,7 +14,12 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { PropsWithChildren, useCallback, useState } from 'react';
|
||||
import React, {
|
||||
PropsWithChildren,
|
||||
useCallback,
|
||||
useMemo,
|
||||
useState,
|
||||
} from 'react';
|
||||
import {
|
||||
DefaultEntityFilters,
|
||||
EntityListContext,
|
||||
@@ -32,6 +37,7 @@ export const MockEntityListContextProvider = ({
|
||||
const [filters, setFilters] = useState<DefaultEntityFilters>(
|
||||
value?.filters ?? {},
|
||||
);
|
||||
|
||||
const updateFilters = useCallback(
|
||||
(
|
||||
update:
|
||||
@@ -49,23 +55,31 @@ export const MockEntityListContextProvider = ({
|
||||
[],
|
||||
);
|
||||
|
||||
const defaultContext: EntityListContextProps = {
|
||||
entities: [],
|
||||
backendEntities: [],
|
||||
updateFilters,
|
||||
filters,
|
||||
loading: false,
|
||||
queryParameters: {},
|
||||
};
|
||||
// Memoize the default values since pickers have useEffect triggers on these; naively defaulting
|
||||
// below with `?? <X>` breaks referential equality on subsequent updates.
|
||||
const defaultValues = useMemo(
|
||||
() => ({
|
||||
entities: [],
|
||||
backendEntities: [],
|
||||
queryParameters: {},
|
||||
}),
|
||||
[],
|
||||
);
|
||||
|
||||
// Extract value.filters to avoid overwriting it; some tests exercise filter updates. The value
|
||||
// provided is used as the initial seed in useState above.
|
||||
const { filters: _, ...otherContextFields } = value ?? {};
|
||||
const resolvedValue: EntityListContextProps = useMemo(
|
||||
() => ({
|
||||
entities: value?.entities ?? defaultValues.entities,
|
||||
backendEntities: value?.backendEntities ?? defaultValues.backendEntities,
|
||||
updateFilters: value?.updateFilters ?? updateFilters,
|
||||
filters,
|
||||
loading: value?.loading ?? false,
|
||||
queryParameters: value?.queryParameters ?? defaultValues.queryParameters,
|
||||
}),
|
||||
[value, defaultValues, filters, updateFilters],
|
||||
);
|
||||
|
||||
return (
|
||||
<EntityListContext.Provider
|
||||
value={{ ...defaultContext, ...otherContextFields }}
|
||||
>
|
||||
<EntityListContext.Provider value={resolvedValue}>
|
||||
{children}
|
||||
</EntityListContext.Provider>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user