Respect queryParameter updates in pickers
Signed-off-by: Tim Hansen <timbonicus@gmail.com>
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-react': patch
|
||||
---
|
||||
|
||||
Updated `useEntityListProvider` and catalog pickers to respond to external changes to query parameters in the URL, such as two sidebar links that apply different catalog filters.
|
||||
+10
-6
@@ -51,15 +51,19 @@ export const EntityLifecyclePicker = () => {
|
||||
const { updateFilters, backendEntities, filters, queryParameters } =
|
||||
useEntityListProvider();
|
||||
|
||||
const queryParamLifecycles = [queryParameters.lifecycles]
|
||||
.flat()
|
||||
.filter(Boolean) as string[];
|
||||
const [selectedLifecycles, setSelectedLifecycles] = useState(
|
||||
queryParamLifecycles.length
|
||||
? queryParamLifecycles
|
||||
: filters.lifecycles?.values ?? [],
|
||||
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]);
|
||||
|
||||
useEffect(() => {
|
||||
updateFilters({
|
||||
lifecycles: selectedLifecycles.length
|
||||
|
||||
@@ -53,13 +53,19 @@ export const EntityOwnerPicker = () => {
|
||||
const { updateFilters, backendEntities, filters, queryParameters } =
|
||||
useEntityListProvider();
|
||||
|
||||
const queryParamOwners = [queryParameters.owners]
|
||||
.flat()
|
||||
.filter(Boolean) as string[];
|
||||
const [selectedOwners, setSelectedOwners] = useState(
|
||||
queryParamOwners.length ? queryParamOwners : filters.owners?.values ?? [],
|
||||
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]);
|
||||
|
||||
useEffect(() => {
|
||||
updateFilters({
|
||||
owners: selectedOwners.length
|
||||
|
||||
@@ -51,12 +51,16 @@ export const EntityTagPicker = () => {
|
||||
const { updateFilters, backendEntities, filters, queryParameters } =
|
||||
useEntityListProvider();
|
||||
|
||||
const queryParamTags = [queryParameters.tags]
|
||||
.flat()
|
||||
.filter(Boolean) as string[];
|
||||
const [selectedTags, setSelectedTags] = useState(
|
||||
queryParamTags.length ? queryParamTags : filters.tags?.values ?? [],
|
||||
);
|
||||
const [selectedTags, setSelectedTags] = useState(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]);
|
||||
|
||||
useEffect(() => {
|
||||
updateFilters({
|
||||
|
||||
@@ -161,9 +161,7 @@ export const UserListPicker = ({
|
||||
[isOwnedEntity, isStarredEntity],
|
||||
);
|
||||
|
||||
const [selectedUserFilter, setSelectedUserFilter] = useState(
|
||||
[queryParameters.user].flat()[0] ?? initialFilter,
|
||||
);
|
||||
const [selectedUserFilter, setSelectedUserFilter] = useState(initialFilter);
|
||||
|
||||
// To show proper counts for each section, apply all other frontend filters _except_ the user
|
||||
// filter that's controlled by this picker.
|
||||
@@ -190,6 +188,13 @@ export const UserListPicker = ({
|
||||
[entitiesWithoutUserFilter, starredFilter, ownedFilter],
|
||||
);
|
||||
|
||||
// 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]);
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
!loading &&
|
||||
|
||||
@@ -25,6 +25,7 @@ import React, {
|
||||
useMemo,
|
||||
useState,
|
||||
} from 'react';
|
||||
import { useLocation } from 'react-router';
|
||||
import useAsyncFn from 'react-use/lib/useAsyncFn';
|
||||
import useDebounce from 'react-use/lib/useDebounce';
|
||||
import useMountedState from 'react-use/lib/useMountedState';
|
||||
@@ -98,7 +99,6 @@ type OutputState<EntityFilters extends DefaultEntityFilters> = {
|
||||
appliedFilters: EntityFilters;
|
||||
entities: Entity[];
|
||||
backendEntities: Entity[];
|
||||
queryParameters: Record<string, string | string[]>;
|
||||
};
|
||||
|
||||
export const EntityListProvider = <EntityFilters extends DefaultEntityFilters>({
|
||||
@@ -109,19 +109,26 @@ export const EntityListProvider = <EntityFilters extends DefaultEntityFilters>({
|
||||
const [requestedFilters, setRequestedFilters] = useState<EntityFilters>(
|
||||
{} as EntityFilters,
|
||||
);
|
||||
|
||||
// We use react-router's useLocation hook so updates from external sources trigger an update to
|
||||
// the queryParameters in outputState. Updates from this hook use replaceState below and won't
|
||||
// trigger a useLocation change; this would instead come from an external source, such as a manual
|
||||
// update of the URL or two catalog sidebar links with different catalog filters.
|
||||
const location = useLocation();
|
||||
const queryParameters = useMemo(
|
||||
() =>
|
||||
(qs.parse(location.search, {
|
||||
ignoreQueryPrefix: true,
|
||||
}).filters ?? {}) as Record<string, string | string[]>,
|
||||
[location],
|
||||
);
|
||||
|
||||
const [outputState, setOutputState] = useState<OutputState<EntityFilters>>(
|
||||
() => {
|
||||
const query = qs.parse(window.location.search, {
|
||||
ignoreQueryPrefix: true,
|
||||
});
|
||||
return {
|
||||
appliedFilters: {} as EntityFilters,
|
||||
entities: [],
|
||||
backendEntities: [],
|
||||
queryParameters: (query.filters ?? {}) as Record<
|
||||
string,
|
||||
string | string[]
|
||||
>,
|
||||
};
|
||||
},
|
||||
);
|
||||
@@ -163,19 +170,17 @@ export const EntityListProvider = <EntityFilters extends DefaultEntityFilters>({
|
||||
appliedFilters: requestedFilters,
|
||||
backendEntities: response.items,
|
||||
entities: response.items.filter(entityFilter),
|
||||
queryParameters: queryParams,
|
||||
});
|
||||
} else {
|
||||
setOutputState({
|
||||
appliedFilters: requestedFilters,
|
||||
backendEntities: outputState.backendEntities,
|
||||
entities: outputState.backendEntities.filter(entityFilter),
|
||||
queryParameters: queryParams,
|
||||
});
|
||||
}
|
||||
|
||||
if (isMounted()) {
|
||||
const oldParams = qs.parse(window.location.search, {
|
||||
const oldParams = qs.parse(location.search, {
|
||||
ignoreQueryPrefix: true,
|
||||
});
|
||||
const newParams = qs.stringify(
|
||||
@@ -191,7 +196,7 @@ export const EntityListProvider = <EntityFilters extends DefaultEntityFilters>({
|
||||
window.history?.replaceState(null, document.title, newUrl);
|
||||
}
|
||||
},
|
||||
[catalogApi, requestedFilters, outputState],
|
||||
[catalogApi, queryParameters, requestedFilters, outputState],
|
||||
{ loading: true },
|
||||
);
|
||||
|
||||
@@ -220,11 +225,11 @@ export const EntityListProvider = <EntityFilters extends DefaultEntityFilters>({
|
||||
entities: outputState.entities,
|
||||
backendEntities: outputState.backendEntities,
|
||||
updateFilters,
|
||||
queryParameters: outputState.queryParameters,
|
||||
queryParameters,
|
||||
loading,
|
||||
error,
|
||||
}),
|
||||
[outputState, updateFilters, loading, error],
|
||||
[outputState, updateFilters, queryParameters, loading, error],
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@@ -42,13 +42,19 @@ export function useEntityTypeFilter(): EntityTypeReturn {
|
||||
updateFilters,
|
||||
} = useEntityListProvider();
|
||||
|
||||
const queryParamTypes = [queryParameters.type]
|
||||
.flat()
|
||||
.filter(Boolean) as string[];
|
||||
const [selectedTypes, setSelectedTypes] = useState(
|
||||
queryParamTypes.length ? queryParamTypes : typeFilter?.getTypes() ?? [],
|
||||
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]);
|
||||
|
||||
const [availableTypes, setAvailableTypes] = useState<string[]>([]);
|
||||
const kind = useMemo(() => kindFilter?.value, [kindFilter]);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user