@backstage/plugin-catalog-react 1.12.4-next.0 → 1.13.0-next.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,51 @@
1
1
  # @backstage/plugin-catalog-react
2
2
 
3
+ ## 1.13.0-next.2
4
+
5
+ ### Minor Changes
6
+
7
+ - 78475c3: Allow offset mode paging in entity list provider
8
+
9
+ ### Patch Changes
10
+
11
+ - c891b69: Add `FavoriteToggle` in `core-components` to standardise favorite marking
12
+ - b537bd7: Allow custom star icons to be provided via the `star` and `unstarred` icon overrides. See how to override existing icons in the [Backstage documentation](https://backstage.io/docs/getting-started/app-custom-theme/#custom-icons).
13
+ - 836127c: Updated dependency `@testing-library/react` to `^16.0.0`.
14
+ - Updated dependencies
15
+ - @backstage/core-components@0.14.11-next.1
16
+ - @backstage/integration-react@1.1.31-next.0
17
+ - @backstage/catalog-client@1.7.0-next.1
18
+ - @backstage/core-compat-api@0.3.0-next.2
19
+ - @backstage/core-plugin-api@1.9.4-next.0
20
+ - @backstage/frontend-plugin-api@0.8.0-next.2
21
+ - @backstage/version-bridge@1.0.9-next.0
22
+ - @backstage/plugin-permission-react@0.4.26-next.0
23
+ - @backstage/catalog-model@1.6.0
24
+ - @backstage/errors@1.2.4
25
+ - @backstage/types@1.1.1
26
+ - @backstage/plugin-catalog-common@1.0.26
27
+ - @backstage/plugin-permission-common@0.8.1
28
+
29
+ ## 1.12.4-next.1
30
+
31
+ ### Patch Changes
32
+
33
+ - ae9b6cb: Small internal fix to better work with recent `lodash` versions
34
+ - Updated dependencies
35
+ - @backstage/frontend-plugin-api@0.8.0-next.1
36
+ - @backstage/core-compat-api@0.3.0-next.1
37
+ - @backstage/catalog-client@1.6.7-next.0
38
+ - @backstage/core-components@0.14.11-next.0
39
+ - @backstage/catalog-model@1.6.0
40
+ - @backstage/core-plugin-api@1.9.3
41
+ - @backstage/errors@1.2.4
42
+ - @backstage/integration-react@1.1.30
43
+ - @backstage/types@1.1.1
44
+ - @backstage/version-bridge@1.0.8
45
+ - @backstage/plugin-catalog-common@1.0.26
46
+ - @backstage/plugin-permission-common@0.8.1
47
+ - @backstage/plugin-permission-react@0.4.25
48
+
3
49
  ## 1.12.4-next.0
4
50
 
5
51
  ### Patch Changes
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backstage/plugin-catalog-react__alpha",
3
- "version": "1.12.4-next.0",
3
+ "version": "1.13.0-next.2",
4
4
  "main": "../dist/alpha.esm.js",
5
5
  "module": "../dist/alpha.esm.js",
6
6
  "types": "../dist/alpha.d.ts"
@@ -25,6 +25,7 @@ function defaultEntityPresentation(entityOrRef, context) {
25
25
  // leave it up to the presentation API to handle
26
26
  };
27
27
  }
28
+ const isString = (value) => Boolean(value) && typeof value === "string";
28
29
  function getParts(entityOrRef) {
29
30
  if (typeof entityOrRef === "string") {
30
31
  let colonI = entityOrRef.indexOf(":");
@@ -38,29 +39,23 @@ function getParts(entityOrRef) {
38
39
  return { kind, namespace, name };
39
40
  }
40
41
  if (typeof entityOrRef === "object" && entityOrRef !== null) {
41
- const kind = [get(entityOrRef, "kind")].find(
42
- (candidate) => candidate && typeof candidate === "string"
43
- );
42
+ const kind = [get(entityOrRef, "kind")].find(isString);
44
43
  const namespace = [
45
44
  get(entityOrRef, "metadata.namespace"),
46
45
  get(entityOrRef, "namespace")
47
- ].find((candidate) => candidate && typeof candidate === "string");
46
+ ].find(isString);
48
47
  const name = [
49
48
  get(entityOrRef, "metadata.name"),
50
49
  get(entityOrRef, "name")
51
- ].find((candidate) => candidate && typeof candidate === "string");
52
- const title = [get(entityOrRef, "metadata.title")].find(
53
- (candidate) => candidate && typeof candidate === "string"
54
- );
50
+ ].find(isString);
51
+ const title = [get(entityOrRef, "metadata.title")].find(isString);
55
52
  const description = [get(entityOrRef, "metadata.description")].find(
56
- (candidate) => candidate && typeof candidate === "string"
53
+ isString
57
54
  );
58
55
  const displayName = [get(entityOrRef, "spec.profile.displayName")].find(
59
- (candidate) => candidate && typeof candidate === "string"
60
- );
61
- const type = [get(entityOrRef, "spec.type")].find(
62
- (candidate) => candidate && typeof candidate === "string"
56
+ isString
63
57
  );
58
+ const type = [get(entityOrRef, "spec.type")].find(isString);
64
59
  return { kind, namespace, name, title, description, displayName, type };
65
60
  }
66
61
  return {};
@@ -1 +1 @@
1
- {"version":3,"file":"defaultEntityPresentation.esm.js","sources":["../../../src/apis/EntityPresentationApi/defaultEntityPresentation.ts"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n CompoundEntityRef,\n DEFAULT_NAMESPACE,\n Entity,\n stringifyEntityRef,\n} from '@backstage/catalog-model';\nimport get from 'lodash/get';\nimport { EntityRefPresentationSnapshot } from './EntityPresentationApi';\n\n/**\n * This returns the default representation of an entity.\n *\n * @public\n * @param entityOrRef - Either an entity, or a ref to it.\n * @param context - Contextual information that may affect the presentation.\n */\nexport function defaultEntityPresentation(\n entityOrRef: Entity | CompoundEntityRef | string,\n context?: {\n defaultKind?: string;\n defaultNamespace?: string;\n },\n): EntityRefPresentationSnapshot {\n // NOTE(freben): This code may look convoluted, but it tries its very best to\n // be defensive and handling any type of malformed input and still producing\n // some form of result without crashing.\n const { kind, namespace, name, title, description, displayName, type } =\n getParts(entityOrRef);\n\n const entityRef: string = stringifyEntityRef({\n kind: kind || 'unknown',\n namespace: namespace || DEFAULT_NAMESPACE,\n name: name || 'unknown',\n });\n\n const shortRef = getShortRef({ kind, namespace, name, context });\n\n const primary = [displayName, title, shortRef].find(\n candidate => candidate && typeof candidate === 'string',\n )!;\n\n const secondary = [\n primary !== entityRef ? entityRef : undefined,\n type,\n description,\n ]\n .filter(candidate => candidate && typeof candidate === 'string')\n .join(' | ');\n\n return {\n entityRef,\n primaryTitle: primary,\n secondaryTitle: secondary || undefined,\n Icon: undefined, // leave it up to the presentation API to handle\n };\n}\n\n// Try to extract display-worthy parts of an entity or ref as best we can, without throwing\nfunction getParts(entityOrRef: Entity | CompoundEntityRef | string): {\n kind?: string;\n namespace?: string;\n name?: string;\n title?: string;\n description?: string;\n displayName?: string;\n type?: string;\n} {\n if (typeof entityOrRef === 'string') {\n let colonI = entityOrRef.indexOf(':');\n const slashI = entityOrRef.indexOf('/');\n\n // If the / is ahead of the :, treat the rest as the name\n if (slashI !== -1 && slashI < colonI) {\n colonI = -1;\n }\n\n const kind = colonI === -1 ? undefined : entityOrRef.slice(0, colonI);\n const namespace =\n slashI === -1 ? undefined : entityOrRef.slice(colonI + 1, slashI);\n const name = entityOrRef.slice(Math.max(colonI + 1, slashI + 1));\n\n return { kind, namespace, name };\n }\n\n if (typeof entityOrRef === 'object' && entityOrRef !== null) {\n const kind = [get(entityOrRef, 'kind')].find(\n candidate => candidate && typeof candidate === 'string',\n );\n\n const namespace = [\n get(entityOrRef, 'metadata.namespace'),\n get(entityOrRef, 'namespace'),\n ].find(candidate => candidate && typeof candidate === 'string');\n\n const name = [\n get(entityOrRef, 'metadata.name'),\n get(entityOrRef, 'name'),\n ].find(candidate => candidate && typeof candidate === 'string');\n\n const title = [get(entityOrRef, 'metadata.title')].find(\n candidate => candidate && typeof candidate === 'string',\n );\n\n const description = [get(entityOrRef, 'metadata.description')].find(\n candidate => candidate && typeof candidate === 'string',\n );\n\n const displayName = [get(entityOrRef, 'spec.profile.displayName')].find(\n candidate => candidate && typeof candidate === 'string',\n );\n\n const type = [get(entityOrRef, 'spec.type')].find(\n candidate => candidate && typeof candidate === 'string',\n );\n\n return { kind, namespace, name, title, description, displayName, type };\n }\n\n return {};\n}\n\nfunction getShortRef(options: {\n kind?: string;\n namespace?: string;\n name?: string;\n context?: { defaultKind?: string; defaultNamespace?: string };\n}): string {\n const kind = options.kind?.toLocaleLowerCase('en-US') || 'unknown';\n const namespace = options.namespace || DEFAULT_NAMESPACE;\n const name = options.name || 'unknown';\n const defaultKindLower =\n options.context?.defaultKind?.toLocaleLowerCase('en-US');\n const defaultNamespaceLower =\n options.context?.defaultNamespace?.toLocaleLowerCase('en-US');\n\n let result = name;\n\n if (\n (defaultNamespaceLower &&\n namespace.toLocaleLowerCase('en-US') !== defaultNamespaceLower) ||\n namespace !== DEFAULT_NAMESPACE\n ) {\n result = `${namespace}/${result}`;\n }\n\n if (\n defaultKindLower &&\n kind.toLocaleLowerCase('en-US') !== defaultKindLower\n ) {\n result = `${kind}:${result}`;\n }\n\n return result;\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"defaultEntityPresentation.esm.js","sources":["../../../src/apis/EntityPresentationApi/defaultEntityPresentation.ts"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n CompoundEntityRef,\n DEFAULT_NAMESPACE,\n Entity,\n stringifyEntityRef,\n} from '@backstage/catalog-model';\nimport get from 'lodash/get';\nimport { EntityRefPresentationSnapshot } from './EntityPresentationApi';\n\n/**\n * This returns the default representation of an entity.\n *\n * @public\n * @param entityOrRef - Either an entity, or a ref to it.\n * @param context - Contextual information that may affect the presentation.\n */\nexport function defaultEntityPresentation(\n entityOrRef: Entity | CompoundEntityRef | string,\n context?: {\n defaultKind?: string;\n defaultNamespace?: string;\n },\n): EntityRefPresentationSnapshot {\n // NOTE(freben): This code may look convoluted, but it tries its very best to\n // be defensive and handling any type of malformed input and still producing\n // some form of result without crashing.\n const { kind, namespace, name, title, description, displayName, type } =\n getParts(entityOrRef);\n\n const entityRef: string = stringifyEntityRef({\n kind: kind || 'unknown',\n namespace: namespace || DEFAULT_NAMESPACE,\n name: name || 'unknown',\n });\n\n const shortRef = getShortRef({ kind, namespace, name, context });\n\n const primary = [displayName, title, shortRef].find(\n candidate => candidate && typeof candidate === 'string',\n )!;\n\n const secondary = [\n primary !== entityRef ? entityRef : undefined,\n type,\n description,\n ]\n .filter(candidate => candidate && typeof candidate === 'string')\n .join(' | ');\n\n return {\n entityRef,\n primaryTitle: primary,\n secondaryTitle: secondary || undefined,\n Icon: undefined, // leave it up to the presentation API to handle\n };\n}\n\nconst isString = (value: unknown): value is string =>\n Boolean(value) && typeof value === 'string';\n\n// Try to extract display-worthy parts of an entity or ref as best we can, without throwing\nfunction getParts(entityOrRef: Entity | CompoundEntityRef | string): {\n kind?: string;\n namespace?: string;\n name?: string;\n title?: string;\n description?: string;\n displayName?: string;\n type?: string;\n} {\n if (typeof entityOrRef === 'string') {\n let colonI = entityOrRef.indexOf(':');\n const slashI = entityOrRef.indexOf('/');\n\n // If the / is ahead of the :, treat the rest as the name\n if (slashI !== -1 && slashI < colonI) {\n colonI = -1;\n }\n\n const kind = colonI === -1 ? undefined : entityOrRef.slice(0, colonI);\n const namespace =\n slashI === -1 ? undefined : entityOrRef.slice(colonI + 1, slashI);\n const name = entityOrRef.slice(Math.max(colonI + 1, slashI + 1));\n\n return { kind, namespace, name };\n }\n\n if (typeof entityOrRef === 'object' && entityOrRef !== null) {\n const kind = [get(entityOrRef, 'kind')].find(isString);\n\n const namespace = [\n get(entityOrRef, 'metadata.namespace'),\n get(entityOrRef, 'namespace'),\n ].find(isString);\n\n const name = [\n get(entityOrRef, 'metadata.name'),\n get(entityOrRef, 'name'),\n ].find(isString);\n\n const title = [get(entityOrRef, 'metadata.title')].find(isString);\n\n const description = [get(entityOrRef, 'metadata.description')].find(\n isString,\n );\n\n const displayName = [get(entityOrRef, 'spec.profile.displayName')].find(\n isString,\n );\n\n const type = [get(entityOrRef, 'spec.type')].find(isString);\n\n return { kind, namespace, name, title, description, displayName, type };\n }\n\n return {};\n}\n\nfunction getShortRef(options: {\n kind?: string;\n namespace?: string;\n name?: string;\n context?: { defaultKind?: string; defaultNamespace?: string };\n}): string {\n const kind = options.kind?.toLocaleLowerCase('en-US') || 'unknown';\n const namespace = options.namespace || DEFAULT_NAMESPACE;\n const name = options.name || 'unknown';\n const defaultKindLower =\n options.context?.defaultKind?.toLocaleLowerCase('en-US');\n const defaultNamespaceLower =\n options.context?.defaultNamespace?.toLocaleLowerCase('en-US');\n\n let result = name;\n\n if (\n (defaultNamespaceLower &&\n namespace.toLocaleLowerCase('en-US') !== defaultNamespaceLower) ||\n namespace !== DEFAULT_NAMESPACE\n ) {\n result = `${namespace}/${result}`;\n }\n\n if (\n defaultKindLower &&\n kind.toLocaleLowerCase('en-US') !== defaultKindLower\n ) {\n result = `${kind}:${result}`;\n }\n\n return result;\n}\n"],"names":[],"mappings":"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"}
@@ -4,6 +4,7 @@ import { catalogApiRef } from '../../api.esm.js';
4
4
  import { useState } from 'react';
5
5
  import get from 'lodash/get';
6
6
 
7
+ const maybeString = (value) => typeof value === "string" ? value : void 0;
7
8
  function useFacetsEntities({ enabled }) {
8
9
  const catalogApi = useApi(catalogApiRef);
9
10
  const [facetsPromise] = useState(async () => {
@@ -20,8 +21,8 @@ function useFacetsEntities({ enabled }) {
20
21
  (a, b) => (a.metadata.namespace || "").localeCompare(
21
22
  b.metadata.namespace || "",
22
23
  "en-US"
23
- ) || (get(a, "spec.profile.displayName") || a.metadata.title || a.metadata.name).localeCompare(
24
- get(b, "spec.profile.displayName") || b.metadata.title || b.metadata.name,
24
+ ) || (maybeString(get(a, "spec.profile.displayName")) || a.metadata.title || a.metadata.name).localeCompare(
25
+ maybeString(get(b, "spec.profile.displayName")) || b.metadata.title || b.metadata.name,
25
26
  "en-US"
26
27
  ) || a.kind.localeCompare(b.kind, "en-US")
27
28
  )
@@ -1 +1 @@
1
- {"version":3,"file":"useFacetsEntities.esm.js","sources":["../../../src/components/EntityOwnerPicker/useFacetsEntities.ts"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { useApi } from '@backstage/core-plugin-api';\nimport useAsyncFn from 'react-use/esm/useAsyncFn';\nimport { catalogApiRef } from '../../api';\nimport { useState } from 'react';\nimport { Entity } from '@backstage/catalog-model';\nimport get from 'lodash/get';\n\ntype FacetsCursor = {\n start: number;\n text: string;\n};\n\ntype FacetsEntitiesResponse = {\n items: Entity[];\n cursor?: string;\n};\n\ntype FacetsInitialRequest = {\n text: string;\n};\n\n/**\n * This hook asynchronously loads the entity owners using the facets endpoint.\n * EntityOwnerPicker uses this hook when mode=\"owners-only\" is passed as prop.\n * All the owners are kept internally in memory and rendered in batches once requested\n * by the frontend. The values returned by this hook are compatible with `useQueryEntities`\n * hook, which is also used by EntityOwnerPicker.\n */\nexport function useFacetsEntities({ enabled }: { enabled: boolean }) {\n const catalogApi = useApi(catalogApiRef);\n\n const [facetsPromise] = useState(async () => {\n if (!enabled) {\n return [];\n }\n const facet = 'relations.ownedBy';\n const facetsResponse = await catalogApi.getEntityFacets({\n facets: [facet],\n });\n const entityRefs = facetsResponse.facets[facet]?.map(e => e.value) ?? [];\n\n return catalogApi\n .getEntitiesByRefs({ entityRefs })\n .then(resp =>\n resp.items\n .filter(entity => entity !== undefined)\n .map(entity => entity as Entity)\n .sort(\n (a, b) =>\n (a.metadata.namespace || '').localeCompare(\n b.metadata.namespace || '',\n 'en-US',\n ) ||\n (\n get(a, 'spec.profile.displayName') ||\n a.metadata.title ||\n a.metadata.name\n ).localeCompare(\n get(b, 'spec.profile.displayName') ||\n b.metadata.title ||\n b.metadata.name,\n 'en-US',\n ) ||\n a.kind.localeCompare(b.kind, 'en-US'),\n ),\n )\n .then(entities => entities)\n .catch(() => []);\n });\n\n return useAsyncFn<\n (\n request: FacetsInitialRequest | FacetsEntitiesResponse,\n options?: { limit?: number },\n ) => Promise<FacetsEntitiesResponse>\n >(\n async (request, options) => {\n const facets = await facetsPromise;\n\n if (!facets) {\n return {\n items: [],\n };\n }\n\n const limit = options?.limit ?? 20;\n\n const { text, start } = decodeCursor(request);\n const filteredRefs = facets.filter(e => filterEntity(text, e));\n const end = start + limit;\n return {\n items: filteredRefs.slice(0, end),\n ...encodeCursor({\n entities: filteredRefs,\n limit: end,\n payload: {\n text,\n start: end,\n },\n }),\n };\n },\n [facetsPromise],\n { loading: true, value: { items: [] } },\n );\n}\n\nfunction decodeCursor(\n request: FacetsInitialRequest | FacetsEntitiesResponse,\n): FacetsCursor {\n if (isFacetsResponse(request) && request.cursor) {\n return JSON.parse(atob(request.cursor));\n }\n return {\n text: (request as FacetsInitialRequest).text || '',\n start: 0,\n };\n}\n\nfunction isFacetsResponse(\n request: FacetsInitialRequest | FacetsEntitiesResponse,\n): request is FacetsEntitiesResponse {\n return !!(request as FacetsEntitiesResponse).cursor;\n}\n\nfunction encodeCursor({\n entities,\n limit,\n payload,\n}: {\n entities: Entity[];\n limit: number;\n payload: { text: string; start: number };\n}) {\n if (entities.length > limit) {\n return { cursor: btoa(JSON.stringify(payload)) };\n }\n return {};\n}\n\nfunction filterEntity(text: string, entity: Entity) {\n const normalizedText = text.trim();\n return (\n entity.kind.includes(normalizedText) ||\n entity.metadata.namespace?.includes(normalizedText) ||\n entity.metadata.name.includes(normalizedText) ||\n entity.metadata.title?.includes(normalizedText) ||\n (get(entity, 'spec.profile.displayName') as unknown as string)?.includes(\n normalizedText,\n )\n );\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useFacetsEntities.esm.js","sources":["../../../src/components/EntityOwnerPicker/useFacetsEntities.ts"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { useApi } from '@backstage/core-plugin-api';\nimport useAsyncFn from 'react-use/esm/useAsyncFn';\nimport { catalogApiRef } from '../../api';\nimport { useState } from 'react';\nimport { Entity } from '@backstage/catalog-model';\nimport get from 'lodash/get';\n\ntype FacetsCursor = {\n start: number;\n text: string;\n};\n\ntype FacetsEntitiesResponse = {\n items: Entity[];\n cursor?: string;\n};\n\ntype FacetsInitialRequest = {\n text: string;\n};\n\nconst maybeString = (value: unknown): string | undefined =>\n typeof value === 'string' ? value : undefined;\n\n/**\n * This hook asynchronously loads the entity owners using the facets endpoint.\n * EntityOwnerPicker uses this hook when mode=\"owners-only\" is passed as prop.\n * All the owners are kept internally in memory and rendered in batches once requested\n * by the frontend. The values returned by this hook are compatible with `useQueryEntities`\n * hook, which is also used by EntityOwnerPicker.\n */\nexport function useFacetsEntities({ enabled }: { enabled: boolean }) {\n const catalogApi = useApi(catalogApiRef);\n\n const [facetsPromise] = useState(async () => {\n if (!enabled) {\n return [];\n }\n const facet = 'relations.ownedBy';\n const facetsResponse = await catalogApi.getEntityFacets({\n facets: [facet],\n });\n const entityRefs = facetsResponse.facets[facet]?.map(e => e.value) ?? [];\n\n return catalogApi\n .getEntitiesByRefs({ entityRefs })\n .then(resp =>\n resp.items\n .filter(entity => entity !== undefined)\n .map(entity => entity as Entity)\n .sort(\n (a, b) =>\n (a.metadata.namespace || '').localeCompare(\n b.metadata.namespace || '',\n 'en-US',\n ) ||\n (\n maybeString(get(a, 'spec.profile.displayName')) ||\n a.metadata.title ||\n a.metadata.name\n ).localeCompare(\n maybeString(get(b, 'spec.profile.displayName')) ||\n b.metadata.title ||\n b.metadata.name,\n 'en-US',\n ) ||\n a.kind.localeCompare(b.kind, 'en-US'),\n ),\n )\n .then(entities => entities)\n .catch(() => []);\n });\n\n return useAsyncFn<\n (\n request: FacetsInitialRequest | FacetsEntitiesResponse,\n options?: { limit?: number },\n ) => Promise<FacetsEntitiesResponse>\n >(\n async (request, options) => {\n const facets = await facetsPromise;\n\n if (!facets) {\n return {\n items: [],\n };\n }\n\n const limit = options?.limit ?? 20;\n\n const { text, start } = decodeCursor(request);\n const filteredRefs = facets.filter(e => filterEntity(text, e));\n const end = start + limit;\n return {\n items: filteredRefs.slice(0, end),\n ...encodeCursor({\n entities: filteredRefs,\n limit: end,\n payload: {\n text,\n start: end,\n },\n }),\n };\n },\n [facetsPromise],\n { loading: true, value: { items: [] } },\n );\n}\n\nfunction decodeCursor(\n request: FacetsInitialRequest | FacetsEntitiesResponse,\n): FacetsCursor {\n if (isFacetsResponse(request) && request.cursor) {\n return JSON.parse(atob(request.cursor));\n }\n return {\n text: (request as FacetsInitialRequest).text || '',\n start: 0,\n };\n}\n\nfunction isFacetsResponse(\n request: FacetsInitialRequest | FacetsEntitiesResponse,\n): request is FacetsEntitiesResponse {\n return !!(request as FacetsEntitiesResponse).cursor;\n}\n\nfunction encodeCursor({\n entities,\n limit,\n payload,\n}: {\n entities: Entity[];\n limit: number;\n payload: { text: string; start: number };\n}) {\n if (entities.length > limit) {\n return { cursor: btoa(JSON.stringify(payload)) };\n }\n return {};\n}\n\nfunction filterEntity(text: string, entity: Entity) {\n const normalizedText = text.trim();\n return (\n entity.kind.includes(normalizedText) ||\n entity.metadata.namespace?.includes(normalizedText) ||\n entity.metadata.name.includes(normalizedText) ||\n entity.metadata.title?.includes(normalizedText) ||\n (get(entity, 'spec.profile.displayName') as unknown as string)?.includes(\n normalizedText,\n )\n );\n}\n"],"names":[],"mappings":"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"}
@@ -1,19 +1,10 @@
1
1
  import { stringifyEntityRef } from '@backstage/catalog-model';
2
- import IconButton from '@material-ui/core/IconButton';
3
- import Tooltip from '@material-ui/core/Tooltip';
4
- import { withStyles } from '@material-ui/core/styles';
5
- import StarIcon from '@material-ui/icons/Star';
6
- import StarBorder from '@material-ui/icons/StarBorder';
7
2
  import React from 'react';
8
3
  import { useStarredEntity } from '../../hooks/useStarredEntity.esm.js';
9
4
  import { catalogReactTranslationRef } from '../../translation.esm.js';
10
5
  import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
6
+ import { FavoriteToggle } from '@backstage/core-components';
11
7
 
12
- const YellowStar = withStyles({
13
- root: {
14
- color: "#f3ba37"
15
- }
16
- })(StarIcon);
17
8
  const FavoriteEntity = (props) => {
18
9
  const { toggleStarredEntity, isStarredEntity } = useStarredEntity(
19
10
  props.entity
@@ -25,15 +16,14 @@ const FavoriteEntity = (props) => {
25
16
  "-"
26
17
  )}`;
27
18
  return /* @__PURE__ */ React.createElement(
28
- IconButton,
19
+ FavoriteToggle,
29
20
  {
30
- "aria-label": title,
21
+ title,
31
22
  id,
32
- color: "inherit",
33
- ...props,
34
- onClick: () => toggleStarredEntity()
35
- },
36
- /* @__PURE__ */ React.createElement(Tooltip, { id, title }, isStarredEntity ? /* @__PURE__ */ React.createElement(YellowStar, null) : /* @__PURE__ */ React.createElement(StarBorder, null))
23
+ isFavorite: isStarredEntity,
24
+ onToggle: toggleStarredEntity,
25
+ ...props
26
+ }
37
27
  );
38
28
  };
39
29
 
@@ -1 +1 @@
1
- {"version":3,"file":"FavoriteEntity.esm.js","sources":["../../../src/components/FavoriteEntity/FavoriteEntity.tsx"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity, stringifyEntityRef } from '@backstage/catalog-model';\nimport IconButton from '@material-ui/core/IconButton';\nimport Tooltip from '@material-ui/core/Tooltip';\nimport { withStyles } from '@material-ui/core/styles';\nimport Star from '@material-ui/icons/Star';\nimport StarBorder from '@material-ui/icons/StarBorder';\nimport React, { ComponentProps } from 'react';\nimport { useStarredEntity } from '../../hooks/useStarredEntity';\nimport { catalogReactTranslationRef } from '../../translation';\nimport { useTranslationRef } from '@backstage/core-plugin-api/alpha';\n\n/** @public */\nexport type FavoriteEntityProps = ComponentProps<typeof IconButton> & {\n entity: Entity;\n};\n\nconst YellowStar = withStyles({\n root: {\n color: '#f3ba37',\n },\n})(Star);\n\n/**\n * IconButton for showing if a current entity is starred and adding/removing it from the favorite entities\n * @param props - MaterialUI IconButton props extended by required `entity` prop\n * @public\n */\nexport const FavoriteEntity = (props: FavoriteEntityProps) => {\n const { toggleStarredEntity, isStarredEntity } = useStarredEntity(\n props.entity,\n );\n const { t } = useTranslationRef(catalogReactTranslationRef);\n const title = isStarredEntity\n ? t('favoriteEntity.removeFromFavorites')\n : t('favoriteEntity.addToFavorites');\n\n const id = `favorite-${stringifyEntityRef(props.entity).replace(\n /[^a-zA-Z0-9-_]/g,\n '-',\n )}`;\n\n return (\n <IconButton\n aria-label={title}\n id={id}\n color=\"inherit\"\n {...props}\n onClick={() => toggleStarredEntity()}\n >\n <Tooltip id={id} title={title}>\n {isStarredEntity ? <YellowStar /> : <StarBorder />}\n </Tooltip>\n </IconButton>\n );\n};\n"],"names":["Star"],"mappings":";;;;;;;;;;;AAgCA,MAAM,aAAa,UAAW,CAAA;AAAA,EAC5B,IAAM,EAAA;AAAA,IACJ,KAAO,EAAA,SAAA;AAAA,GACT;AACF,CAAC,EAAEA,QAAI,CAAA,CAAA;AAOM,MAAA,cAAA,GAAiB,CAAC,KAA+B,KAAA;AAC5D,EAAM,MAAA,EAAE,mBAAqB,EAAA,eAAA,EAAoB,GAAA,gBAAA;AAAA,IAC/C,KAAM,CAAA,MAAA;AAAA,GACR,CAAA;AACA,EAAA,MAAM,EAAE,CAAA,EAAM,GAAA,iBAAA,CAAkB,0BAA0B,CAAA,CAAA;AAC1D,EAAA,MAAM,QAAQ,eACV,GAAA,CAAA,CAAE,oCAAoC,CAAA,GACtC,EAAE,+BAA+B,CAAA,CAAA;AAErC,EAAA,MAAM,EAAK,GAAA,CAAA,SAAA,EAAY,kBAAmB,CAAA,KAAA,CAAM,MAAM,CAAE,CAAA,OAAA;AAAA,IACtD,iBAAA;AAAA,IACA,GAAA;AAAA,GACD,CAAA,CAAA,CAAA;AAED,EACE,uBAAA,KAAA,CAAA,aAAA;AAAA,IAAC,UAAA;AAAA,IAAA;AAAA,MACC,YAAY,EAAA,KAAA;AAAA,MACZ,EAAA;AAAA,MACA,KAAM,EAAA,SAAA;AAAA,MACL,GAAG,KAAA;AAAA,MACJ,OAAA,EAAS,MAAM,mBAAoB,EAAA;AAAA,KAAA;AAAA,oBAEnC,KAAA,CAAA,aAAA,CAAC,OAAQ,EAAA,EAAA,EAAA,EAAQ,KACd,EAAA,EAAA,eAAA,uCAAmB,UAAW,EAAA,IAAA,CAAA,mBAAM,KAAA,CAAA,aAAA,CAAA,UAAA,EAAA,IAAW,CAClD,CAAA;AAAA,GACF,CAAA;AAEJ;;;;"}
1
+ {"version":3,"file":"FavoriteEntity.esm.js","sources":["../../../src/components/FavoriteEntity/FavoriteEntity.tsx"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity, stringifyEntityRef } from '@backstage/catalog-model';\nimport IconButton from '@material-ui/core/IconButton';\nimport React, { ComponentProps } from 'react';\nimport { useStarredEntity } from '../../hooks/useStarredEntity';\nimport { catalogReactTranslationRef } from '../../translation';\nimport { useTranslationRef } from '@backstage/core-plugin-api/alpha';\nimport { FavoriteToggle } from '@backstage/core-components';\n\n/** @public */\nexport type FavoriteEntityProps = ComponentProps<typeof IconButton> & {\n entity: Entity;\n};\n\n/**\n * IconButton for showing if a current entity is starred and adding/removing it from the favorite entities\n * @param props - MaterialUI IconButton props extended by required `entity` prop\n * @public\n */\nexport const FavoriteEntity = (props: FavoriteEntityProps) => {\n const { toggleStarredEntity, isStarredEntity } = useStarredEntity(\n props.entity,\n );\n const { t } = useTranslationRef(catalogReactTranslationRef);\n const title = isStarredEntity\n ? t('favoriteEntity.removeFromFavorites')\n : t('favoriteEntity.addToFavorites');\n\n const id = `favorite-${stringifyEntityRef(props.entity).replace(\n /[^a-zA-Z0-9-_]/g,\n '-',\n )}`;\n\n return (\n <FavoriteToggle\n title={title}\n id={id}\n isFavorite={isStarredEntity}\n onToggle={toggleStarredEntity}\n {...props}\n />\n );\n};\n"],"names":[],"mappings":";;;;;;;AAkCa,MAAA,cAAA,GAAiB,CAAC,KAA+B,KAAA;AAC5D,EAAM,MAAA,EAAE,mBAAqB,EAAA,eAAA,EAAoB,GAAA,gBAAA;AAAA,IAC/C,KAAM,CAAA,MAAA;AAAA,GACR,CAAA;AACA,EAAA,MAAM,EAAE,CAAA,EAAM,GAAA,iBAAA,CAAkB,0BAA0B,CAAA,CAAA;AAC1D,EAAA,MAAM,QAAQ,eACV,GAAA,CAAA,CAAE,oCAAoC,CAAA,GACtC,EAAE,+BAA+B,CAAA,CAAA;AAErC,EAAA,MAAM,EAAK,GAAA,CAAA,SAAA,EAAY,kBAAmB,CAAA,KAAA,CAAM,MAAM,CAAE,CAAA,OAAA;AAAA,IACtD,iBAAA;AAAA,IACA,GAAA;AAAA,GACD,CAAA,CAAA,CAAA;AAED,EACE,uBAAA,KAAA,CAAA,aAAA;AAAA,IAAC,cAAA;AAAA,IAAA;AAAA,MACC,KAAA;AAAA,MACA,EAAA;AAAA,MACA,UAAY,EAAA,eAAA;AAAA,MACZ,QAAU,EAAA,mBAAA;AAAA,MACT,GAAG,KAAA;AAAA,KAAA;AAAA,GACN,CAAA;AAEJ;;;;"}
@@ -8,7 +8,7 @@ import MenuItem from '@material-ui/core/MenuItem';
8
8
  import Typography from '@material-ui/core/Typography';
9
9
  import { makeStyles } from '@material-ui/core/styles';
10
10
  import SettingsIcon from '@material-ui/icons/Settings';
11
- import StarIcon from '@material-ui/icons/Star';
11
+ import { StarIcon } from '@backstage/core-components';
12
12
  import React, { useMemo, useState, useEffect, Fragment } from 'react';
13
13
  import { EntityUserFilter } from '../../filters.esm.js';
14
14
  import '../../hooks/useEntity.esm.js';
@@ -1 +1 @@
1
- {"version":3,"file":"UserListPicker.esm.js","sources":["../../../src/components/UserListPicker/UserListPicker.tsx"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n configApiRef,\n IconComponent,\n useApi,\n} from '@backstage/core-plugin-api';\nimport Card from '@material-ui/core/Card';\nimport List from '@material-ui/core/List';\nimport ListItemIcon from '@material-ui/core/ListItemIcon';\nimport ListItemSecondaryAction from '@material-ui/core/ListItemSecondaryAction';\nimport ListItemText from '@material-ui/core/ListItemText';\nimport MenuItem from '@material-ui/core/MenuItem';\nimport Typography from '@material-ui/core/Typography';\nimport { makeStyles } from '@material-ui/core/styles';\nimport SettingsIcon from '@material-ui/icons/Settings';\nimport StarIcon from '@material-ui/icons/Star';\nimport React, { Fragment, useEffect, useMemo, useState } from 'react';\nimport { EntityUserFilter } from '../../filters';\nimport { useEntityList } from '../../hooks';\nimport { UserListFilterKind } from '../../types';\nimport { useOwnedEntitiesCount } from './useOwnedEntitiesCount';\nimport { useAllEntitiesCount } from './useAllEntitiesCount';\nimport { useStarredEntitiesCount } from './useStarredEntitiesCount';\nimport {\n TranslationFunction,\n useTranslationRef,\n} from '@backstage/core-plugin-api/alpha';\nimport { catalogReactTranslationRef } from '../../translation';\n\n/** @public */\nexport type CatalogReactUserListPickerClassKey =\n | 'root'\n | 'title'\n | 'listIcon'\n | 'menuItem'\n | 'groupWrapper';\n\nconst useStyles = makeStyles(\n theme => ({\n root: {\n backgroundColor: 'rgba(0, 0, 0, .11)',\n boxShadow: 'none',\n margin: theme.spacing(1, 0, 1, 0),\n },\n title: {\n margin: theme.spacing(1, 0, 0, 1),\n textTransform: 'uppercase',\n fontSize: 12,\n fontWeight: 'bold',\n },\n listIcon: {\n minWidth: 30,\n color: theme.palette.text.primary,\n },\n menuItem: {\n minHeight: theme.spacing(6),\n },\n groupWrapper: {\n margin: theme.spacing(1, 1, 2, 1),\n },\n }),\n {\n name: 'CatalogReactUserListPicker',\n },\n);\n\nexport type ButtonGroup = {\n name: string;\n items: {\n id: 'owned' | 'starred' | 'all';\n label: string;\n icon?: IconComponent;\n }[];\n};\n\nfunction getFilterGroups(\n orgName: string,\n t: TranslationFunction<typeof catalogReactTranslationRef.T>,\n): ButtonGroup[] {\n return [\n {\n name: t('userListPicker.personalFilter.title'),\n items: [\n {\n id: 'owned',\n label: t('userListPicker.personalFilter.ownedLabel'),\n icon: SettingsIcon,\n },\n {\n id: 'starred',\n label: t('userListPicker.personalFilter.starredLabel'),\n icon: StarIcon,\n },\n ],\n },\n {\n name: orgName,\n items: [\n {\n id: 'all',\n label: t('userListPicker.orgFilterAllLabel'),\n },\n ],\n },\n ];\n}\n\n/** @public */\nexport type UserListPickerProps = {\n initialFilter?: UserListFilterKind;\n availableFilters?: UserListFilterKind[];\n};\n\n/** @public */\nexport const UserListPicker = (props: UserListPickerProps) => {\n const { initialFilter, availableFilters } = props;\n const classes = useStyles();\n const configApi = useApi(configApiRef);\n const { t } = useTranslationRef(catalogReactTranslationRef);\n const orgName =\n configApi.getOptionalString('organization.name') ??\n t('userListPicker.defaultOrgName');\n const {\n filters,\n updateFilters,\n queryParameters: { kind: kindParameter, user: userParameter },\n } = useEntityList();\n\n // Remove group items that aren't in availableFilters and exclude\n // any now-empty groups.\n const userAndGroupFilterIds = ['starred', 'all'];\n const filterGroups = getFilterGroups(orgName, t)\n .map(filterGroup => ({\n ...filterGroup,\n items: filterGroup.items.filter(({ id }) =>\n // TODO: avoid hardcoding kinds here\n ['group', 'user'].some(kind => kind === kindParameter)\n ? userAndGroupFilterIds.includes(id)\n : !availableFilters || availableFilters.includes(id),\n ),\n }))\n .filter(({ items }) => !!items.length);\n\n const {\n count: ownedEntitiesCount,\n loading: loadingOwnedEntities,\n filter: ownedEntitiesFilter,\n } = useOwnedEntitiesCount();\n const { count: allCount } = useAllEntitiesCount();\n const {\n count: starredEntitiesCount,\n filter: starredEntitiesFilter,\n loading: loadingStarredEntities,\n } = useStarredEntitiesCount();\n\n const queryParamUserFilter = useMemo(\n () => [userParameter].flat()[0],\n [userParameter],\n );\n\n const [selectedUserFilter, setSelectedUserFilter] = useState(\n (queryParamUserFilter as UserListFilterKind) ?? initialFilter,\n );\n\n const filterCounts = useMemo(() => {\n return {\n all: allCount,\n starred: starredEntitiesCount,\n owned: ownedEntitiesCount,\n };\n }, [starredEntitiesCount, ownedEntitiesCount, allCount]);\n\n // Set selected user filter on query parameter updates; this happens at initial page load and from\n // external updates to the page location.\n useEffect(() => {\n if (queryParamUserFilter) {\n setSelectedUserFilter(queryParamUserFilter as UserListFilterKind);\n }\n }, [queryParamUserFilter]);\n\n const loading = loadingOwnedEntities || loadingStarredEntities;\n\n useEffect(() => {\n if (\n !loading &&\n !!selectedUserFilter &&\n selectedUserFilter !== 'all' &&\n filterCounts[selectedUserFilter] === 0\n ) {\n setSelectedUserFilter('all');\n }\n }, [loading, filterCounts, selectedUserFilter, setSelectedUserFilter]);\n\n useEffect(() => {\n if (!selectedUserFilter) {\n return;\n }\n if (loading) {\n return;\n }\n\n const getFilter = () => {\n if (selectedUserFilter === 'owned') {\n return ownedEntitiesFilter;\n }\n if (selectedUserFilter === 'starred') {\n return starredEntitiesFilter;\n }\n return EntityUserFilter.all();\n };\n\n updateFilters({ user: getFilter() });\n }, [\n selectedUserFilter,\n starredEntitiesFilter,\n ownedEntitiesFilter,\n updateFilters,\n\n loading,\n ]);\n\n return (\n <Card className={classes.root}>\n {filterGroups.map(group => (\n <Fragment key={group.name}>\n <Typography\n variant=\"subtitle2\"\n component=\"span\"\n className={classes.title}\n >\n {group.name}\n </Typography>\n <Card className={classes.groupWrapper}>\n <List disablePadding dense role=\"menu\" aria-label={group.name}>\n {group.items.map((item, index) => (\n <MenuItem\n role=\"none presentation\"\n key={item.id}\n divider={index !== group.items.length - 1}\n onClick={() => setSelectedUserFilter(item.id)}\n selected={item.id === filters.user?.value}\n className={classes.menuItem}\n disabled={filterCounts[item.id] === 0}\n data-testid={`user-picker-${item.id}`}\n tabIndex={0}\n ContainerProps={{ role: 'menuitem' }}\n >\n {item.icon && (\n <ListItemIcon className={classes.listIcon}>\n <item.icon fontSize=\"small\" />\n </ListItemIcon>\n )}\n <ListItemText>\n <Typography variant=\"body1\">{item.label} </Typography>\n </ListItemText>\n <ListItemSecondaryAction>\n {filterCounts[item.id] ?? '-'}\n </ListItemSecondaryAction>\n </MenuItem>\n ))}\n </List>\n </Card>\n </Fragment>\n ))}\n </Card>\n );\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"UserListPicker.esm.js","sources":["../../../src/components/UserListPicker/UserListPicker.tsx"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n configApiRef,\n IconComponent,\n useApi,\n} from '@backstage/core-plugin-api';\nimport Card from '@material-ui/core/Card';\nimport List from '@material-ui/core/List';\nimport ListItemIcon from '@material-ui/core/ListItemIcon';\nimport ListItemSecondaryAction from '@material-ui/core/ListItemSecondaryAction';\nimport ListItemText from '@material-ui/core/ListItemText';\nimport MenuItem from '@material-ui/core/MenuItem';\nimport Typography from '@material-ui/core/Typography';\nimport { makeStyles } from '@material-ui/core/styles';\nimport SettingsIcon from '@material-ui/icons/Settings';\nimport { StarIcon } from '@backstage/core-components';\nimport React, { Fragment, useEffect, useMemo, useState } from 'react';\nimport { EntityUserFilter } from '../../filters';\nimport { useEntityList } from '../../hooks';\nimport { UserListFilterKind } from '../../types';\nimport { useOwnedEntitiesCount } from './useOwnedEntitiesCount';\nimport { useAllEntitiesCount } from './useAllEntitiesCount';\nimport { useStarredEntitiesCount } from './useStarredEntitiesCount';\nimport {\n TranslationFunction,\n useTranslationRef,\n} from '@backstage/core-plugin-api/alpha';\nimport { catalogReactTranslationRef } from '../../translation';\n\n/** @public */\nexport type CatalogReactUserListPickerClassKey =\n | 'root'\n | 'title'\n | 'listIcon'\n | 'menuItem'\n | 'groupWrapper';\n\nconst useStyles = makeStyles(\n theme => ({\n root: {\n backgroundColor: 'rgba(0, 0, 0, .11)',\n boxShadow: 'none',\n margin: theme.spacing(1, 0, 1, 0),\n },\n title: {\n margin: theme.spacing(1, 0, 0, 1),\n textTransform: 'uppercase',\n fontSize: 12,\n fontWeight: 'bold',\n },\n listIcon: {\n minWidth: 30,\n color: theme.palette.text.primary,\n },\n menuItem: {\n minHeight: theme.spacing(6),\n },\n groupWrapper: {\n margin: theme.spacing(1, 1, 2, 1),\n },\n }),\n {\n name: 'CatalogReactUserListPicker',\n },\n);\n\nexport type ButtonGroup = {\n name: string;\n items: {\n id: 'owned' | 'starred' | 'all';\n label: string;\n icon?: IconComponent;\n }[];\n};\n\nfunction getFilterGroups(\n orgName: string,\n t: TranslationFunction<typeof catalogReactTranslationRef.T>,\n): ButtonGroup[] {\n return [\n {\n name: t('userListPicker.personalFilter.title'),\n items: [\n {\n id: 'owned',\n label: t('userListPicker.personalFilter.ownedLabel'),\n icon: SettingsIcon,\n },\n {\n id: 'starred',\n label: t('userListPicker.personalFilter.starredLabel'),\n icon: StarIcon,\n },\n ],\n },\n {\n name: orgName,\n items: [\n {\n id: 'all',\n label: t('userListPicker.orgFilterAllLabel'),\n },\n ],\n },\n ];\n}\n\n/** @public */\nexport type UserListPickerProps = {\n initialFilter?: UserListFilterKind;\n availableFilters?: UserListFilterKind[];\n};\n\n/** @public */\nexport const UserListPicker = (props: UserListPickerProps) => {\n const { initialFilter, availableFilters } = props;\n const classes = useStyles();\n const configApi = useApi(configApiRef);\n const { t } = useTranslationRef(catalogReactTranslationRef);\n const orgName =\n configApi.getOptionalString('organization.name') ??\n t('userListPicker.defaultOrgName');\n const {\n filters,\n updateFilters,\n queryParameters: { kind: kindParameter, user: userParameter },\n } = useEntityList();\n\n // Remove group items that aren't in availableFilters and exclude\n // any now-empty groups.\n const userAndGroupFilterIds = ['starred', 'all'];\n const filterGroups = getFilterGroups(orgName, t)\n .map(filterGroup => ({\n ...filterGroup,\n items: filterGroup.items.filter(({ id }) =>\n // TODO: avoid hardcoding kinds here\n ['group', 'user'].some(kind => kind === kindParameter)\n ? userAndGroupFilterIds.includes(id)\n : !availableFilters || availableFilters.includes(id),\n ),\n }))\n .filter(({ items }) => !!items.length);\n\n const {\n count: ownedEntitiesCount,\n loading: loadingOwnedEntities,\n filter: ownedEntitiesFilter,\n } = useOwnedEntitiesCount();\n const { count: allCount } = useAllEntitiesCount();\n const {\n count: starredEntitiesCount,\n filter: starredEntitiesFilter,\n loading: loadingStarredEntities,\n } = useStarredEntitiesCount();\n\n const queryParamUserFilter = useMemo(\n () => [userParameter].flat()[0],\n [userParameter],\n );\n\n const [selectedUserFilter, setSelectedUserFilter] = useState(\n (queryParamUserFilter as UserListFilterKind) ?? initialFilter,\n );\n\n const filterCounts = useMemo(() => {\n return {\n all: allCount,\n starred: starredEntitiesCount,\n owned: ownedEntitiesCount,\n };\n }, [starredEntitiesCount, ownedEntitiesCount, allCount]);\n\n // Set selected user filter on query parameter updates; this happens at initial page load and from\n // external updates to the page location.\n useEffect(() => {\n if (queryParamUserFilter) {\n setSelectedUserFilter(queryParamUserFilter as UserListFilterKind);\n }\n }, [queryParamUserFilter]);\n\n const loading = loadingOwnedEntities || loadingStarredEntities;\n\n useEffect(() => {\n if (\n !loading &&\n !!selectedUserFilter &&\n selectedUserFilter !== 'all' &&\n filterCounts[selectedUserFilter] === 0\n ) {\n setSelectedUserFilter('all');\n }\n }, [loading, filterCounts, selectedUserFilter, setSelectedUserFilter]);\n\n useEffect(() => {\n if (!selectedUserFilter) {\n return;\n }\n if (loading) {\n return;\n }\n\n const getFilter = () => {\n if (selectedUserFilter === 'owned') {\n return ownedEntitiesFilter;\n }\n if (selectedUserFilter === 'starred') {\n return starredEntitiesFilter;\n }\n return EntityUserFilter.all();\n };\n\n updateFilters({ user: getFilter() });\n }, [\n selectedUserFilter,\n starredEntitiesFilter,\n ownedEntitiesFilter,\n updateFilters,\n\n loading,\n ]);\n\n return (\n <Card className={classes.root}>\n {filterGroups.map(group => (\n <Fragment key={group.name}>\n <Typography\n variant=\"subtitle2\"\n component=\"span\"\n className={classes.title}\n >\n {group.name}\n </Typography>\n <Card className={classes.groupWrapper}>\n <List disablePadding dense role=\"menu\" aria-label={group.name}>\n {group.items.map((item, index) => (\n <MenuItem\n role=\"none presentation\"\n key={item.id}\n divider={index !== group.items.length - 1}\n onClick={() => setSelectedUserFilter(item.id)}\n selected={item.id === filters.user?.value}\n className={classes.menuItem}\n disabled={filterCounts[item.id] === 0}\n data-testid={`user-picker-${item.id}`}\n tabIndex={0}\n ContainerProps={{ role: 'menuitem' }}\n >\n {item.icon && (\n <ListItemIcon className={classes.listIcon}>\n <item.icon fontSize=\"small\" />\n </ListItemIcon>\n )}\n <ListItemText>\n <Typography variant=\"body1\">{item.label} </Typography>\n </ListItemText>\n <ListItemSecondaryAction>\n {filterCounts[item.id] ?? '-'}\n </ListItemSecondaryAction>\n </MenuItem>\n ))}\n </List>\n </Card>\n </Fragment>\n ))}\n </Card>\n );\n};\n"],"names":[],"mappings":"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"}
@@ -18,25 +18,50 @@ const EntityListProvider = (props) => {
18
18
  {}
19
19
  );
20
20
  const location = useLocation();
21
- const enablePagination = props.pagination === true || typeof props.pagination === "object";
22
- const limit = props.pagination && typeof props.pagination === "object" && typeof props.pagination.limit === "number" ? props.pagination.limit : 20;
23
- const { queryParameters, cursor: initialCursor } = useMemo(() => {
21
+ const getPaginationMode = () => {
22
+ if (props.pagination === true) {
23
+ return "cursor";
24
+ }
25
+ return typeof props.pagination === "object" ? props.pagination.mode ?? "cursor" : "none";
26
+ };
27
+ const paginationMode = getPaginationMode();
28
+ const paginationLimit = typeof props.pagination === "object" ? props.pagination.limit ?? 20 : 20;
29
+ const {
30
+ queryParameters,
31
+ cursor: initialCursor,
32
+ offset: initialOffset,
33
+ limit: initialLimit
34
+ } = useMemo(() => {
24
35
  const parsed = qs.parse(location.search, {
25
36
  ignoreQueryPrefix: true
26
37
  });
38
+ let limit2 = paginationLimit;
39
+ if (typeof parsed.limit === "string") {
40
+ const queryLimit = Number.parseInt(parsed.limit, 10);
41
+ if (!isNaN(queryLimit)) {
42
+ limit2 = queryLimit;
43
+ }
44
+ }
45
+ const offset2 = typeof parsed.offset === "string" && paginationMode === "offset" ? Number.parseInt(parsed.offset, 10) : void 0;
27
46
  return {
28
47
  queryParameters: parsed.filters ?? {},
29
- cursor: typeof parsed.cursor === "string" ? parsed.cursor : void 0
48
+ cursor: typeof parsed.cursor === "string" && paginationMode === "cursor" ? parsed.cursor : void 0,
49
+ offset: paginationMode === "offset" && offset2 && !isNaN(offset2) ? offset2 : void 0,
50
+ limit: limit2
30
51
  };
31
- }, [location]);
52
+ }, [paginationMode, location.search, paginationLimit]);
32
53
  const [cursor, setCursor] = useState(initialCursor);
54
+ const [offset, setOffset] = useState(initialOffset);
55
+ const [limit, setLimit] = useState(initialLimit);
33
56
  const [outputState, setOutputState] = useState(
34
57
  () => {
35
58
  return {
36
59
  appliedFilters: {},
37
60
  entities: [],
38
61
  backendEntities: [],
39
- pageInfo: enablePagination ? {} : void 0
62
+ pageInfo: paginationMode === "cursor" ? {} : void 0,
63
+ offset,
64
+ limit
40
65
  };
41
66
  }
42
67
  );
@@ -53,7 +78,7 @@ const EntityListProvider = (props) => {
53
78
  },
54
79
  {}
55
80
  );
56
- if (enablePagination) {
81
+ if (paginationMode !== "none") {
57
82
  if (cursor) {
58
83
  if (cursor !== outputState.appliedCursor) {
59
84
  const entityFilter = reduceEntityFilters(compacted);
@@ -76,10 +101,11 @@ const EntityListProvider = (props) => {
76
101
  const previousBackendFilter = reduceCatalogFilters(
77
102
  compact(Object.values(outputState.appliedFilters))
78
103
  );
79
- if (!isEqual(previousBackendFilter, backendFilter)) {
104
+ if (paginationMode === "offset" || !isEqual(previousBackendFilter, backendFilter)) {
80
105
  const response = await catalogApi.queryEntities({
81
106
  ...backendFilter,
82
107
  limit,
108
+ offset,
83
109
  orderFields: [{ field: "metadata.name", order: "asc" }]
84
110
  });
85
111
  setOutputState({
@@ -87,7 +113,9 @@ const EntityListProvider = (props) => {
87
113
  backendEntities: response.items,
88
114
  entities: response.items.filter(entityFilter),
89
115
  pageInfo: response.pageInfo,
90
- totalItems: response.totalItems
116
+ totalItems: response.totalItems,
117
+ limit,
118
+ offset
91
119
  });
92
120
  }
93
121
  }
@@ -123,7 +151,7 @@ const EntityListProvider = (props) => {
123
151
  ignoreQueryPrefix: true
124
152
  });
125
153
  const newParams = qs.stringify(
126
- { ...oldParams, filters: queryParams, cursor },
154
+ { ...oldParams, filters: queryParams, cursor, offset, limit },
127
155
  { addQueryPrefix: true, arrayFormat: "repeat" }
128
156
  );
129
157
  const newUrl = `${window.location.pathname}${newParams}`;
@@ -136,11 +164,13 @@ const EntityListProvider = (props) => {
136
164
  requestedFilters,
137
165
  outputState,
138
166
  cursor,
139
- enablePagination
167
+ paginationMode,
168
+ limit,
169
+ offset
140
170
  ],
141
171
  { loading: true }
142
172
  );
143
- useDebounce(refresh, 10, [requestedFilters, cursor]);
173
+ useDebounce(refresh, 10, [requestedFilters, cursor, limit, offset]);
144
174
  const updateFilters = useCallback(
145
175
  (update) => {
146
176
  setCursor(void 0);
@@ -152,7 +182,7 @@ const EntityListProvider = (props) => {
152
182
  []
153
183
  );
154
184
  const pageInfo = useMemo(() => {
155
- if (!enablePagination) {
185
+ if (paginationMode !== "cursor") {
156
186
  return void 0;
157
187
  }
158
188
  const prevCursor = outputState.pageInfo?.prevCursor;
@@ -161,7 +191,7 @@ const EntityListProvider = (props) => {
161
191
  prev: prevCursor ? () => setCursor(prevCursor) : void 0,
162
192
  next: nextCursor ? () => setCursor(nextCursor) : void 0
163
193
  };
164
- }, [enablePagination, outputState.pageInfo]);
194
+ }, [paginationMode, outputState.pageInfo]);
165
195
  const value = useMemo(
166
196
  () => ({
167
197
  filters: outputState.appliedFilters,
@@ -172,9 +202,26 @@ const EntityListProvider = (props) => {
172
202
  loading,
173
203
  error,
174
204
  pageInfo,
175
- totalItems: outputState.totalItems
205
+ totalItems: outputState.totalItems,
206
+ limit,
207
+ offset,
208
+ setLimit,
209
+ setOffset,
210
+ paginationMode
176
211
  }),
177
- [outputState, updateFilters, queryParameters, loading, error, pageInfo]
212
+ [
213
+ outputState,
214
+ updateFilters,
215
+ queryParameters,
216
+ loading,
217
+ error,
218
+ pageInfo,
219
+ limit,
220
+ offset,
221
+ paginationMode,
222
+ setLimit,
223
+ setOffset
224
+ ]
178
225
  );
179
226
  return /* @__PURE__ */ React.createElement(EntityListContext.Provider, { value }, props.children);
180
227
  };
@@ -1 +1 @@
1
- {"version":3,"file":"useEntityListProvider.esm.js","sources":["../../src/hooks/useEntityListProvider.tsx"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity } from '@backstage/catalog-model';\nimport { compact, isEqual } from 'lodash';\nimport qs from 'qs';\nimport React, {\n createContext,\n PropsWithChildren,\n useCallback,\n useContext,\n useMemo,\n useState,\n} from 'react';\nimport { useLocation } from 'react-router-dom';\nimport useAsyncFn from 'react-use/esm/useAsyncFn';\nimport useDebounce from 'react-use/esm/useDebounce';\nimport useMountedState from 'react-use/esm/useMountedState';\nimport { catalogApiRef } from '../api';\nimport {\n EntityErrorFilter,\n EntityKindFilter,\n EntityLifecycleFilter,\n EntityOrphanFilter,\n EntityOwnerFilter,\n EntityTagFilter,\n EntityTextFilter,\n EntityTypeFilter,\n UserListFilter,\n EntityNamespaceFilter,\n EntityUserFilter,\n} from '../filters';\nimport { EntityFilter } from '../types';\nimport {\n reduceBackendCatalogFilters,\n reduceCatalogFilters,\n reduceEntityFilters,\n} from '../utils';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { QueryEntitiesResponse } from '@backstage/catalog-client';\n\n/** @public */\nexport type DefaultEntityFilters = {\n kind?: EntityKindFilter;\n type?: EntityTypeFilter;\n user?: UserListFilter | EntityUserFilter;\n owners?: EntityOwnerFilter;\n lifecycles?: EntityLifecycleFilter;\n tags?: EntityTagFilter;\n text?: EntityTextFilter;\n orphan?: EntityOrphanFilter;\n error?: EntityErrorFilter;\n namespace?: EntityNamespaceFilter;\n};\n\n/** @public */\nexport type EntityListContextProps<\n EntityFilters extends DefaultEntityFilters = DefaultEntityFilters,\n> = {\n /**\n * The currently registered filters, adhering to the shape of DefaultEntityFilters or an extension\n * of that default (to add custom filter types).\n */\n filters: EntityFilters;\n\n /**\n * The resolved list of catalog entities, after all filters are applied.\n */\n entities: Entity[];\n\n /**\n * The resolved list of catalog entities, after _only catalog-backend_ filters are applied.\n */\n backendEntities: Entity[];\n\n /**\n * Update one or more of the registered filters. Optional filters can be set to `undefined` to\n * reset the filter.\n */\n updateFilters: (\n filters:\n | Partial<EntityFilters>\n | ((prevFilters: EntityFilters) => Partial<EntityFilters>),\n ) => void;\n\n /**\n * Filter values from query parameters.\n */\n queryParameters: Partial<Record<keyof EntityFilters, string | string[]>>;\n\n loading: boolean;\n error?: Error;\n\n pageInfo?: {\n next?: () => void;\n prev?: () => void;\n };\n\n totalItems?: number;\n};\n\n/**\n * Creates new context for entity listing and filtering.\n * @public\n */\nexport const EntityListContext = createContext<\n EntityListContextProps<any> | undefined\n>(undefined);\n\ntype OutputState<EntityFilters extends DefaultEntityFilters> = {\n appliedFilters: EntityFilters;\n appliedCursor?: string;\n entities: Entity[];\n backendEntities: Entity[];\n pageInfo?: QueryEntitiesResponse['pageInfo'];\n totalItems?: number;\n};\n\n/**\n * @public\n */\nexport type EntityListProviderProps = PropsWithChildren<{\n pagination?: boolean | { limit?: number };\n}>;\n\n/**\n * Provides entities and filters for a catalog listing.\n * @public\n */\nexport const EntityListProvider = <EntityFilters extends DefaultEntityFilters>(\n props: EntityListProviderProps,\n) => {\n const isMounted = useMountedState();\n const catalogApi = useApi(catalogApiRef);\n const [requestedFilters, setRequestedFilters] = useState<EntityFilters>(\n {} as EntityFilters,\n );\n\n // We use react-router's useLocation hook so updates from external sources trigger an update to\n // the queryParameters in outputState. Updates from this hook use replaceState below and won't\n // trigger a useLocation change; this would instead come from an external source, such as a manual\n // update of the URL or two catalog sidebar links with different catalog filters.\n const location = useLocation();\n\n const enablePagination =\n props.pagination === true || typeof props.pagination === 'object';\n\n const limit =\n props.pagination &&\n typeof props.pagination === 'object' &&\n typeof props.pagination.limit === 'number'\n ? props.pagination.limit\n : 20;\n\n const { queryParameters, cursor: initialCursor } = useMemo(() => {\n const parsed = qs.parse(location.search, {\n ignoreQueryPrefix: true,\n });\n\n return {\n queryParameters: (parsed.filters ?? {}) as Record<\n string,\n string | string[]\n >,\n cursor: typeof parsed.cursor === 'string' ? parsed.cursor : undefined,\n };\n }, [location]);\n\n const [cursor, setCursor] = useState(initialCursor);\n\n const [outputState, setOutputState] = useState<OutputState<EntityFilters>>(\n () => {\n return {\n appliedFilters: {} as EntityFilters,\n entities: [],\n backendEntities: [],\n pageInfo: enablePagination ? {} : undefined,\n };\n },\n );\n\n // The main async filter worker. Note that while it has a lot of dependencies\n // in terms of its implementation, the triggering only happens (debounced)\n // based on the requested filters changing.\n const [{ loading, error }, refresh] = useAsyncFn(\n async () => {\n const compacted = compact(Object.values(requestedFilters));\n\n const queryParams = Object.keys(requestedFilters).reduce(\n (params, key) => {\n const filter = requestedFilters[key as keyof EntityFilters] as\n | EntityFilter\n | undefined;\n if (filter?.toQueryValue) {\n params[key] = filter.toQueryValue();\n }\n return params;\n },\n {} as Record<string, string | string[]>,\n );\n\n if (enablePagination) {\n if (cursor) {\n if (cursor !== outputState.appliedCursor) {\n const entityFilter = reduceEntityFilters(compacted);\n const response = await catalogApi.queryEntities({\n cursor,\n limit,\n });\n setOutputState({\n appliedFilters: requestedFilters,\n appliedCursor: cursor,\n backendEntities: response.items,\n entities: response.items.filter(entityFilter),\n pageInfo: response.pageInfo,\n totalItems: response.totalItems,\n });\n }\n } else {\n const entityFilter = reduceEntityFilters(compacted);\n const backendFilter = reduceCatalogFilters(compacted);\n const previousBackendFilter = reduceCatalogFilters(\n compact(Object.values(outputState.appliedFilters)),\n );\n\n if (!isEqual(previousBackendFilter, backendFilter)) {\n const response = await catalogApi.queryEntities({\n ...backendFilter,\n limit,\n orderFields: [{ field: 'metadata.name', order: 'asc' }],\n });\n setOutputState({\n appliedFilters: requestedFilters,\n backendEntities: response.items,\n entities: response.items.filter(entityFilter),\n pageInfo: response.pageInfo,\n totalItems: response.totalItems,\n });\n }\n }\n } else {\n const entityFilter = reduceEntityFilters(compacted);\n const backendFilter = reduceBackendCatalogFilters(compacted);\n const previousBackendFilter = reduceBackendCatalogFilters(\n compact(Object.values(outputState.appliedFilters)),\n );\n\n // TODO(mtlewis): currently entities will never be requested unless\n // there's at least one filter, we should allow an initial request\n // to happen with no filters.\n if (!isEqual(previousBackendFilter, backendFilter)) {\n // TODO(timbonicus): should limit fields here, but would need filter\n // fields + table columns\n const response = await catalogApi.getEntities({\n filter: backendFilter,\n });\n const entities = response.items.filter(entityFilter);\n setOutputState({\n appliedFilters: requestedFilters,\n backendEntities: response.items,\n entities,\n totalItems: entities.length,\n });\n } else {\n const entities = outputState.backendEntities.filter(entityFilter);\n setOutputState({\n appliedFilters: requestedFilters,\n backendEntities: outputState.backendEntities,\n entities,\n totalItems: entities.length,\n });\n }\n }\n\n if (isMounted()) {\n const oldParams = qs.parse(location.search, {\n ignoreQueryPrefix: true,\n });\n const newParams = qs.stringify(\n { ...oldParams, filters: queryParams, cursor },\n { addQueryPrefix: true, arrayFormat: 'repeat' },\n );\n const newUrl = `${window.location.pathname}${newParams}`;\n // We use direct history manipulation since useSearchParams and\n // useNavigate in react-router-dom cause unnecessary extra rerenders.\n // Also make sure to replace the state rather than pushing, since we\n // don't want there to be back/forward slots for every single filter\n // change.\n window.history?.replaceState(null, document.title, newUrl);\n }\n },\n [\n catalogApi,\n queryParameters,\n requestedFilters,\n outputState,\n cursor,\n enablePagination,\n ],\n { loading: true },\n );\n\n // Slight debounce on the refresh, since (especially on page load) several\n // filters will be calling this in rapid succession.\n useDebounce(refresh, 10, [requestedFilters, cursor]);\n\n const updateFilters = useCallback(\n (\n update:\n | Partial<EntityFilter>\n | ((prevFilters: EntityFilters) => Partial<EntityFilters>),\n ) => {\n // changing filters will affect pagination, so we need to reset\n // the cursor and start from the first page.\n // TODO(vinzscam): this is currently causing issues at page reload\n // where the state is not kept. Unfortunately we need to rethink\n // the way filters work in order to fix this.\n setCursor(undefined);\n setRequestedFilters(prevFilters => {\n const newFilters =\n typeof update === 'function' ? update(prevFilters) : update;\n return { ...prevFilters, ...newFilters };\n });\n },\n [],\n );\n\n const pageInfo = useMemo(() => {\n if (!enablePagination) {\n return undefined;\n }\n\n const prevCursor = outputState.pageInfo?.prevCursor;\n const nextCursor = outputState.pageInfo?.nextCursor;\n return {\n prev: prevCursor ? () => setCursor(prevCursor) : undefined,\n next: nextCursor ? () => setCursor(nextCursor) : undefined,\n };\n }, [enablePagination, outputState.pageInfo]);\n\n const value = useMemo(\n () => ({\n filters: outputState.appliedFilters,\n entities: outputState.entities,\n backendEntities: outputState.backendEntities,\n updateFilters,\n queryParameters,\n loading,\n error,\n pageInfo,\n totalItems: outputState.totalItems,\n }),\n [outputState, updateFilters, queryParameters, loading, error, pageInfo],\n );\n\n return (\n <EntityListContext.Provider value={value}>\n {props.children}\n </EntityListContext.Provider>\n );\n};\n\n/**\n * Hook for interacting with the entity list context provided by the {@link EntityListProvider}.\n * @public\n */\nexport function useEntityList<\n EntityFilters extends DefaultEntityFilters = DefaultEntityFilters,\n>(): EntityListContextProps<EntityFilters> {\n const context = useContext(EntityListContext);\n if (!context)\n throw new Error('useEntityList must be used within EntityListProvider');\n return context;\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useEntityListProvider.esm.js","sources":["../../src/hooks/useEntityListProvider.tsx"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity } from '@backstage/catalog-model';\nimport { compact, isEqual } from 'lodash';\nimport qs from 'qs';\nimport React, {\n createContext,\n PropsWithChildren,\n useCallback,\n useContext,\n useMemo,\n useState,\n} from 'react';\nimport { useLocation } from 'react-router-dom';\nimport useAsyncFn from 'react-use/esm/useAsyncFn';\nimport useDebounce from 'react-use/esm/useDebounce';\nimport useMountedState from 'react-use/esm/useMountedState';\nimport { catalogApiRef } from '../api';\nimport {\n EntityErrorFilter,\n EntityKindFilter,\n EntityLifecycleFilter,\n EntityNamespaceFilter,\n EntityOrphanFilter,\n EntityOwnerFilter,\n EntityTagFilter,\n EntityTextFilter,\n EntityTypeFilter,\n EntityUserFilter,\n UserListFilter,\n} from '../filters';\nimport { EntityFilter, EntityListPagination } from '../types';\nimport {\n reduceBackendCatalogFilters,\n reduceCatalogFilters,\n reduceEntityFilters,\n} from '../utils';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { QueryEntitiesResponse } from '@backstage/catalog-client';\n\n/** @public */\nexport type DefaultEntityFilters = {\n kind?: EntityKindFilter;\n type?: EntityTypeFilter;\n user?: UserListFilter | EntityUserFilter;\n owners?: EntityOwnerFilter;\n lifecycles?: EntityLifecycleFilter;\n tags?: EntityTagFilter;\n text?: EntityTextFilter;\n orphan?: EntityOrphanFilter;\n error?: EntityErrorFilter;\n namespace?: EntityNamespaceFilter;\n};\n\n/** @public */\nexport type PaginationMode = 'cursor' | 'offset' | 'none';\n\n/** @public */\nexport type EntityListContextProps<\n EntityFilters extends DefaultEntityFilters = DefaultEntityFilters,\n> = {\n /**\n * The currently registered filters, adhering to the shape of DefaultEntityFilters or an extension\n * of that default (to add custom filter types).\n */\n filters: EntityFilters;\n\n /**\n * The resolved list of catalog entities, after all filters are applied.\n */\n entities: Entity[];\n\n /**\n * The resolved list of catalog entities, after _only catalog-backend_ filters are applied.\n */\n backendEntities: Entity[];\n\n /**\n * Update one or more of the registered filters. Optional filters can be set to `undefined` to\n * reset the filter.\n */\n updateFilters: (\n filters:\n | Partial<EntityFilters>\n | ((prevFilters: EntityFilters) => Partial<EntityFilters>),\n ) => void;\n\n /**\n * Filter values from query parameters.\n */\n queryParameters: Partial<Record<keyof EntityFilters, string | string[]>>;\n\n loading: boolean;\n error?: Error;\n\n pageInfo?: {\n next?: () => void;\n prev?: () => void;\n };\n totalItems?: number;\n limit: number;\n offset?: number;\n setLimit: (limit: number) => void;\n setOffset?: (offset: number) => void;\n paginationMode: PaginationMode;\n};\n\n/**\n * Creates new context for entity listing and filtering.\n * @public\n */\nexport const EntityListContext = createContext<\n EntityListContextProps<any> | undefined\n>(undefined);\n\ntype OutputState<EntityFilters extends DefaultEntityFilters> = {\n appliedFilters: EntityFilters;\n appliedCursor?: string;\n entities: Entity[];\n backendEntities: Entity[];\n pageInfo?: QueryEntitiesResponse['pageInfo'];\n totalItems?: number;\n offset?: number;\n limit?: number;\n};\n\n/**\n * @public\n */\nexport type EntityListProviderProps = PropsWithChildren<{\n pagination?: EntityListPagination;\n}>;\n\n/**\n * Provides entities and filters for a catalog listing.\n * @public\n */\nexport const EntityListProvider = <EntityFilters extends DefaultEntityFilters>(\n props: EntityListProviderProps,\n) => {\n const isMounted = useMountedState();\n const catalogApi = useApi(catalogApiRef);\n const [requestedFilters, setRequestedFilters] = useState<EntityFilters>(\n {} as EntityFilters,\n );\n\n // We use react-router's useLocation hook so updates from external sources trigger an update to\n // the queryParameters in outputState. Updates from this hook use replaceState below and won't\n // trigger a useLocation change; this would instead come from an external source, such as a manual\n // update of the URL or two catalog sidebar links with different catalog filters.\n const location = useLocation();\n\n const getPaginationMode = (): PaginationMode => {\n if (props.pagination === true) {\n return 'cursor';\n }\n return typeof props.pagination === 'object'\n ? props.pagination.mode ?? 'cursor'\n : 'none';\n };\n\n const paginationMode: PaginationMode = getPaginationMode();\n const paginationLimit =\n typeof props.pagination === 'object' ? props.pagination.limit ?? 20 : 20;\n\n const {\n queryParameters,\n cursor: initialCursor,\n offset: initialOffset,\n limit: initialLimit,\n } = useMemo(() => {\n const parsed = qs.parse(location.search, {\n ignoreQueryPrefix: true,\n });\n\n let limit = paginationLimit;\n if (typeof parsed.limit === 'string') {\n const queryLimit = Number.parseInt(parsed.limit, 10);\n if (!isNaN(queryLimit)) {\n limit = queryLimit;\n }\n }\n\n const offset =\n typeof parsed.offset === 'string' && paginationMode === 'offset'\n ? Number.parseInt(parsed.offset, 10)\n : undefined;\n\n return {\n queryParameters: (parsed.filters ?? {}) as Record<\n string,\n string | string[]\n >,\n cursor:\n typeof parsed.cursor === 'string' && paginationMode === 'cursor'\n ? parsed.cursor\n : undefined,\n offset:\n paginationMode === 'offset' && offset && !isNaN(offset)\n ? offset\n : undefined,\n limit,\n };\n }, [paginationMode, location.search, paginationLimit]);\n\n const [cursor, setCursor] = useState(initialCursor);\n const [offset, setOffset] = useState<number | undefined>(initialOffset);\n const [limit, setLimit] = useState(initialLimit);\n\n const [outputState, setOutputState] = useState<OutputState<EntityFilters>>(\n () => {\n return {\n appliedFilters: {} as EntityFilters,\n entities: [],\n backendEntities: [],\n pageInfo: paginationMode === 'cursor' ? {} : undefined,\n offset,\n limit,\n };\n },\n );\n\n // The main async filter worker. Note that while it has a lot of dependencies\n // in terms of its implementation, the triggering only happens (debounced)\n // based on the requested filters changing.\n const [{ loading, error }, refresh] = useAsyncFn(\n async () => {\n const compacted = compact(Object.values(requestedFilters));\n\n const queryParams = Object.keys(requestedFilters).reduce(\n (params, key) => {\n const filter = requestedFilters[key as keyof EntityFilters] as\n | EntityFilter\n | undefined;\n if (filter?.toQueryValue) {\n params[key] = filter.toQueryValue();\n }\n return params;\n },\n {} as Record<string, string | string[]>,\n );\n\n if (paginationMode !== 'none') {\n if (cursor) {\n if (cursor !== outputState.appliedCursor) {\n const entityFilter = reduceEntityFilters(compacted);\n const response = await catalogApi.queryEntities({\n cursor,\n limit,\n });\n setOutputState({\n appliedFilters: requestedFilters,\n appliedCursor: cursor,\n backendEntities: response.items,\n entities: response.items.filter(entityFilter),\n pageInfo: response.pageInfo,\n totalItems: response.totalItems,\n });\n }\n } else {\n const entityFilter = reduceEntityFilters(compacted);\n const backendFilter = reduceCatalogFilters(compacted);\n const previousBackendFilter = reduceCatalogFilters(\n compact(Object.values(outputState.appliedFilters)),\n );\n\n if (\n paginationMode === 'offset' ||\n !isEqual(previousBackendFilter, backendFilter)\n ) {\n const response = await catalogApi.queryEntities({\n ...backendFilter,\n limit,\n offset,\n orderFields: [{ field: 'metadata.name', order: 'asc' }],\n });\n setOutputState({\n appliedFilters: requestedFilters,\n backendEntities: response.items,\n entities: response.items.filter(entityFilter),\n pageInfo: response.pageInfo,\n totalItems: response.totalItems,\n limit,\n offset,\n });\n }\n }\n } else {\n const entityFilter = reduceEntityFilters(compacted);\n const backendFilter = reduceBackendCatalogFilters(compacted);\n const previousBackendFilter = reduceBackendCatalogFilters(\n compact(Object.values(outputState.appliedFilters)),\n );\n\n // TODO(mtlewis): currently entities will never be requested unless\n // there's at least one filter, we should allow an initial request\n // to happen with no filters.\n if (!isEqual(previousBackendFilter, backendFilter)) {\n // TODO(timbonicus): should limit fields here, but would need filter\n // fields + table columns\n const response = await catalogApi.getEntities({\n filter: backendFilter,\n });\n const entities = response.items.filter(entityFilter);\n setOutputState({\n appliedFilters: requestedFilters,\n backendEntities: response.items,\n entities,\n totalItems: entities.length,\n });\n } else {\n const entities = outputState.backendEntities.filter(entityFilter);\n setOutputState({\n appliedFilters: requestedFilters,\n backendEntities: outputState.backendEntities,\n entities,\n totalItems: entities.length,\n });\n }\n }\n\n if (isMounted()) {\n const oldParams = qs.parse(location.search, {\n ignoreQueryPrefix: true,\n });\n const newParams = qs.stringify(\n { ...oldParams, filters: queryParams, cursor, offset, limit },\n { addQueryPrefix: true, arrayFormat: 'repeat' },\n );\n const newUrl = `${window.location.pathname}${newParams}`;\n // We use direct history manipulation since useSearchParams and\n // useNavigate in react-router-dom cause unnecessary extra rerenders.\n // Also make sure to replace the state rather than pushing, since we\n // don't want there to be back/forward slots for every single filter\n // change.\n window.history?.replaceState(null, document.title, newUrl);\n }\n },\n [\n catalogApi,\n queryParameters,\n requestedFilters,\n outputState,\n cursor,\n paginationMode,\n limit,\n offset,\n ],\n { loading: true },\n );\n\n // Slight debounce on the refresh, since (especially on page load) several\n // filters will be calling this in rapid succession.\n useDebounce(refresh, 10, [requestedFilters, cursor, limit, offset]);\n\n const updateFilters = useCallback(\n (\n update:\n | Partial<EntityFilter>\n | ((prevFilters: EntityFilters) => Partial<EntityFilters>),\n ) => {\n // changing filters will affect pagination, so we need to reset\n // the cursor and start from the first page.\n // TODO(vinzscam): this is currently causing issues at page reload\n // where the state is not kept. Unfortunately we need to rethink\n // the way filters work in order to fix this.\n setCursor(undefined);\n setRequestedFilters(prevFilters => {\n const newFilters =\n typeof update === 'function' ? update(prevFilters) : update;\n return { ...prevFilters, ...newFilters };\n });\n },\n [],\n );\n\n const pageInfo = useMemo(() => {\n if (paginationMode !== 'cursor') {\n return undefined;\n }\n\n const prevCursor = outputState.pageInfo?.prevCursor;\n const nextCursor = outputState.pageInfo?.nextCursor;\n return {\n prev: prevCursor ? () => setCursor(prevCursor) : undefined,\n next: nextCursor ? () => setCursor(nextCursor) : undefined,\n };\n }, [paginationMode, outputState.pageInfo]);\n\n const value = useMemo(\n () => ({\n filters: outputState.appliedFilters,\n entities: outputState.entities,\n backendEntities: outputState.backendEntities,\n updateFilters,\n queryParameters,\n loading,\n error,\n pageInfo,\n totalItems: outputState.totalItems,\n limit,\n offset,\n setLimit,\n setOffset,\n paginationMode,\n }),\n [\n outputState,\n updateFilters,\n queryParameters,\n loading,\n error,\n pageInfo,\n limit,\n offset,\n paginationMode,\n setLimit,\n setOffset,\n ],\n );\n\n return (\n <EntityListContext.Provider value={value}>\n {props.children}\n </EntityListContext.Provider>\n );\n};\n\n/**\n * Hook for interacting with the entity list context provided by the {@link EntityListProvider}.\n * @public\n */\nexport function useEntityList<\n EntityFilters extends DefaultEntityFilters = DefaultEntityFilters,\n>(): EntityListContextProps<EntityFilters> {\n const context = useContext(EntityListContext);\n if (!context)\n throw new Error('useEntityList must be used within EntityListProvider');\n return context;\n}\n"],"names":["limit","offset"],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -239,6 +239,15 @@ type EntityFilter = {
239
239
  };
240
240
  /** @public */
241
241
  type UserListFilterKind = 'owned' | 'starred' | 'all';
242
+ /** @public */
243
+ type EntityListPagination = boolean | {
244
+ mode?: 'cursor';
245
+ limit?: number;
246
+ } | {
247
+ mode: 'offset';
248
+ limit?: number;
249
+ offset?: number;
250
+ };
242
251
 
243
252
  /** @public */
244
253
  type CatalogReactEntityOwnerPickerClassKey = 'input';
@@ -685,6 +694,8 @@ type DefaultEntityFilters = {
685
694
  namespace?: EntityNamespaceFilter;
686
695
  };
687
696
  /** @public */
697
+ type PaginationMode = 'cursor' | 'offset' | 'none';
698
+ /** @public */
688
699
  type EntityListContextProps<EntityFilters extends DefaultEntityFilters = DefaultEntityFilters> = {
689
700
  /**
690
701
  * The currently registered filters, adhering to the shape of DefaultEntityFilters or an extension
@@ -715,6 +726,11 @@ type EntityListContextProps<EntityFilters extends DefaultEntityFilters = Default
715
726
  prev?: () => void;
716
727
  };
717
728
  totalItems?: number;
729
+ limit: number;
730
+ offset?: number;
731
+ setLimit: (limit: number) => void;
732
+ setOffset?: (offset: number) => void;
733
+ paginationMode: PaginationMode;
718
734
  };
719
735
  /**
720
736
  * Creates new context for entity listing and filtering.
@@ -725,9 +741,7 @@ declare const EntityListContext: React__default.Context<EntityListContextProps<a
725
741
  * @public
726
742
  */
727
743
  type EntityListProviderProps = PropsWithChildren<{
728
- pagination?: boolean | {
729
- limit?: number;
730
- };
744
+ pagination?: EntityListPagination;
731
745
  }>;
732
746
  /**
733
747
  * Provides entities and filters for a catalog listing.
@@ -954,4 +968,4 @@ type EntitySourceLocation = {
954
968
  /** @public */
955
969
  declare function getEntitySourceLocation(entity: Entity, scmIntegrationsApi: typeof scmIntegrationsApiRef.T): EntitySourceLocation | undefined;
956
970
 
957
- export { type AllowedEntityFilters, AsyncEntityProvider, type AsyncEntityProviderProps, type BackstageOverrides, CatalogFilterLayout, type CatalogReactComponentsNameToClassKey, type CatalogReactEntityAutocompletePickerClassKey, type CatalogReactEntityDisplayNameClassKey, type CatalogReactEntityLifecyclePickerClassKey, type CatalogReactEntityNamespacePickerClassKey, type CatalogReactEntityOwnerPickerClassKey, type CatalogReactEntityProcessingStatusPickerClassKey, type CatalogReactEntitySearchBarClassKey, type CatalogReactEntityTagPickerClassKey, type CatalogReactUserListPickerClassKey, type DefaultEntityFilters, DefaultFilters, type DefaultFiltersProps, EntityAutocompletePicker, type EntityAutocompletePickerProps, EntityDisplayName, type EntityDisplayNameProps, EntityErrorFilter, type EntityFilter, EntityKindFilter, EntityKindPicker, type EntityKindPickerProps, EntityLifecycleFilter, EntityLifecyclePicker, EntityListContext, type EntityListContextProps, EntityListProvider, type EntityListProviderProps, type EntityLoadingStatus, EntityNamespaceFilter, EntityNamespacePicker, type EntityNamespacePickerProps, EntityOrphanFilter, EntityOwnerFilter, EntityOwnerPicker, type EntityOwnerPickerProps, EntityPeekAheadPopover, type EntityPeekAheadPopoverProps, type EntityPresentationApi, EntityProcessingStatusPicker, EntityProvider, type EntityProviderProps, EntityRefLink, type EntityRefLinkProps, EntityRefLinks, type EntityRefLinksProps, type EntityRefPresentation, type EntityRefPresentationSnapshot, EntitySearchBar, type EntitySourceLocation, EntityTable, type EntityTableProps, EntityTagFilter, EntityTagPicker, type EntityTagPickerProps, EntityTextFilter, EntityTypeFilter, EntityTypePicker, type EntityTypePickerProps, EntityUserFilter, FavoriteEntity, type FavoriteEntityProps, InspectEntityDialog, MissingAnnotationEmptyState, type MissingAnnotationEmptyStateClassKey, MockEntityListContextProvider, MockStarredEntitiesApi, type StarredEntitiesApi, UnregisterEntityDialog, type UnregisterEntityDialogProps, UserListFilter, type UserListFilterKind, UserListPicker, type UserListPickerProps, catalogApiRef, columnFactories, defaultEntityPresentation, entityPresentationApiRef, entityRouteParams, entityRouteRef, getEntityRelations, getEntitySourceLocation, humanizeEntityRef, starredEntitiesApiRef, useAsyncEntity, useEntity, useEntityList, useEntityOwnership, useEntityPresentation, useEntityTypeFilter, useRelatedEntities, useStarredEntities, useStarredEntity };
971
+ export { type AllowedEntityFilters, AsyncEntityProvider, type AsyncEntityProviderProps, type BackstageOverrides, CatalogFilterLayout, type CatalogReactComponentsNameToClassKey, type CatalogReactEntityAutocompletePickerClassKey, type CatalogReactEntityDisplayNameClassKey, type CatalogReactEntityLifecyclePickerClassKey, type CatalogReactEntityNamespacePickerClassKey, type CatalogReactEntityOwnerPickerClassKey, type CatalogReactEntityProcessingStatusPickerClassKey, type CatalogReactEntitySearchBarClassKey, type CatalogReactEntityTagPickerClassKey, type CatalogReactUserListPickerClassKey, type DefaultEntityFilters, DefaultFilters, type DefaultFiltersProps, EntityAutocompletePicker, type EntityAutocompletePickerProps, EntityDisplayName, type EntityDisplayNameProps, EntityErrorFilter, type EntityFilter, EntityKindFilter, EntityKindPicker, type EntityKindPickerProps, EntityLifecycleFilter, EntityLifecyclePicker, EntityListContext, type EntityListContextProps, type EntityListPagination, EntityListProvider, type EntityListProviderProps, type EntityLoadingStatus, EntityNamespaceFilter, EntityNamespacePicker, type EntityNamespacePickerProps, EntityOrphanFilter, EntityOwnerFilter, EntityOwnerPicker, type EntityOwnerPickerProps, EntityPeekAheadPopover, type EntityPeekAheadPopoverProps, type EntityPresentationApi, EntityProcessingStatusPicker, EntityProvider, type EntityProviderProps, EntityRefLink, type EntityRefLinkProps, EntityRefLinks, type EntityRefLinksProps, type EntityRefPresentation, type EntityRefPresentationSnapshot, EntitySearchBar, type EntitySourceLocation, EntityTable, type EntityTableProps, EntityTagFilter, EntityTagPicker, type EntityTagPickerProps, EntityTextFilter, EntityTypeFilter, EntityTypePicker, type EntityTypePickerProps, EntityUserFilter, FavoriteEntity, type FavoriteEntityProps, InspectEntityDialog, MissingAnnotationEmptyState, type MissingAnnotationEmptyStateClassKey, MockEntityListContextProvider, MockStarredEntitiesApi, type PaginationMode, type StarredEntitiesApi, UnregisterEntityDialog, type UnregisterEntityDialogProps, UserListFilter, type UserListFilterKind, UserListPicker, type UserListPickerProps, catalogApiRef, columnFactories, defaultEntityPresentation, entityPresentationApiRef, entityRouteParams, entityRouteRef, getEntityRelations, getEntitySourceLocation, humanizeEntityRef, starredEntitiesApiRef, useAsyncEntity, useEntity, useEntityList, useEntityOwnership, useEntityPresentation, useEntityTypeFilter, useRelatedEntities, useStarredEntities, useStarredEntity };
@@ -30,7 +30,13 @@ function MockEntityListContextProvider(props) {
30
30
  loading: value?.loading ?? false,
31
31
  queryParameters: value?.queryParameters ?? defaultValues.queryParameters,
32
32
  error: value?.error,
33
- totalItems: value?.totalItems ?? (value?.entities ?? defaultValues.entities).length
33
+ totalItems: value?.totalItems ?? (value?.entities ?? defaultValues.entities).length,
34
+ limit: value?.limit ?? 20,
35
+ offset: value?.offset,
36
+ setLimit: value?.setLimit ?? (() => {
37
+ }),
38
+ setOffset: value?.setOffset,
39
+ paginationMode: value?.paginationMode ?? "none"
34
40
  }),
35
41
  [value, defaultValues, filters, updateFilters]
36
42
  );
@@ -1 +1 @@
1
- {"version":3,"file":"providers.esm.js","sources":["../../src/testUtils/providers.tsx"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React, {\n PropsWithChildren,\n useCallback,\n useMemo,\n useState,\n} from 'react';\nimport {\n DefaultEntityFilters,\n EntityListContext,\n EntityListContextProps,\n} from '../hooks/useEntityListProvider';\n\n/** @public */\nexport function MockEntityListContextProvider<\n T extends DefaultEntityFilters = DefaultEntityFilters,\n>(\n props: PropsWithChildren<{\n value?: Partial<EntityListContextProps<T>>;\n }>,\n) {\n const { children, value } = props;\n\n // Provides a default implementation that stores filter state, for testing components that\n // reflect filter state.\n const [filters, setFilters] = useState<T>(value?.filters ?? ({} as T));\n\n const updateFilters = useCallback(\n (update: Partial<T> | ((prevFilters: T) => Partial<T>)) => {\n setFilters(prevFilters => {\n const newFilters =\n typeof update === 'function' ? update(prevFilters) : update;\n return { ...prevFilters, ...newFilters };\n });\n },\n [],\n );\n\n // Memoize the default values since pickers have useEffect triggers on these; naively defaulting\n // below with `?? <X>` breaks referential equality on subsequent updates.\n const defaultValues = useMemo(\n () => ({\n entities: [],\n backendEntities: [],\n queryParameters: {},\n }),\n [],\n );\n\n const resolvedValue: EntityListContextProps<T> = useMemo(\n () => ({\n entities: value?.entities ?? defaultValues.entities,\n backendEntities: value?.backendEntities ?? defaultValues.backendEntities,\n updateFilters: value?.updateFilters ?? updateFilters,\n filters,\n loading: value?.loading ?? false,\n queryParameters: value?.queryParameters ?? defaultValues.queryParameters,\n error: value?.error,\n totalItems:\n value?.totalItems ?? (value?.entities ?? defaultValues.entities).length,\n }),\n [value, defaultValues, filters, updateFilters],\n );\n\n return (\n <EntityListContext.Provider value={resolvedValue}>\n {children}\n </EntityListContext.Provider>\n );\n}\n"],"names":[],"mappings":";;;AA6BO,SAAS,8BAGd,KAGA,EAAA;AACA,EAAM,MAAA,EAAE,QAAU,EAAA,KAAA,EAAU,GAAA,KAAA,CAAA;AAI5B,EAAM,MAAA,CAAC,SAAS,UAAU,CAAA,GAAI,SAAY,KAAO,EAAA,OAAA,IAAY,EAAQ,CAAA,CAAA;AAErE,EAAA,MAAM,aAAgB,GAAA,WAAA;AAAA,IACpB,CAAC,MAA0D,KAAA;AACzD,MAAA,UAAA,CAAW,CAAe,WAAA,KAAA;AACxB,QAAA,MAAM,aACJ,OAAO,MAAA,KAAW,UAAa,GAAA,MAAA,CAAO,WAAW,CAAI,GAAA,MAAA,CAAA;AACvD,QAAA,OAAO,EAAE,GAAG,WAAa,EAAA,GAAG,UAAW,EAAA,CAAA;AAAA,OACxC,CAAA,CAAA;AAAA,KACH;AAAA,IACA,EAAC;AAAA,GACH,CAAA;AAIA,EAAA,MAAM,aAAgB,GAAA,OAAA;AAAA,IACpB,OAAO;AAAA,MACL,UAAU,EAAC;AAAA,MACX,iBAAiB,EAAC;AAAA,MAClB,iBAAiB,EAAC;AAAA,KACpB,CAAA;AAAA,IACA,EAAC;AAAA,GACH,CAAA;AAEA,EAAA,MAAM,aAA2C,GAAA,OAAA;AAAA,IAC/C,OAAO;AAAA,MACL,QAAA,EAAU,KAAO,EAAA,QAAA,IAAY,aAAc,CAAA,QAAA;AAAA,MAC3C,eAAA,EAAiB,KAAO,EAAA,eAAA,IAAmB,aAAc,CAAA,eAAA;AAAA,MACzD,aAAA,EAAe,OAAO,aAAiB,IAAA,aAAA;AAAA,MACvC,OAAA;AAAA,MACA,OAAA,EAAS,OAAO,OAAW,IAAA,KAAA;AAAA,MAC3B,eAAA,EAAiB,KAAO,EAAA,eAAA,IAAmB,aAAc,CAAA,eAAA;AAAA,MACzD,OAAO,KAAO,EAAA,KAAA;AAAA,MACd,YACE,KAAO,EAAA,UAAA,IAAA,CAAe,KAAO,EAAA,QAAA,IAAY,cAAc,QAAU,EAAA,MAAA;AAAA,KACrE,CAAA;AAAA,IACA,CAAC,KAAA,EAAO,aAAe,EAAA,OAAA,EAAS,aAAa,CAAA;AAAA,GAC/C,CAAA;AAEA,EAAA,2CACG,iBAAkB,CAAA,QAAA,EAAlB,EAA2B,KAAA,EAAO,iBAChC,QACH,CAAA,CAAA;AAEJ;;;;"}
1
+ {"version":3,"file":"providers.esm.js","sources":["../../src/testUtils/providers.tsx"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React, {\n PropsWithChildren,\n useCallback,\n useMemo,\n useState,\n} from 'react';\nimport {\n DefaultEntityFilters,\n EntityListContext,\n EntityListContextProps,\n} from '../hooks/useEntityListProvider';\n\n/** @public */\nexport function MockEntityListContextProvider<\n T extends DefaultEntityFilters = DefaultEntityFilters,\n>(\n props: PropsWithChildren<{\n value?: Partial<EntityListContextProps<T>>;\n }>,\n) {\n const { children, value } = props;\n\n // Provides a default implementation that stores filter state, for testing components that\n // reflect filter state.\n const [filters, setFilters] = useState<T>(value?.filters ?? ({} as T));\n\n const updateFilters = useCallback(\n (update: Partial<T> | ((prevFilters: T) => Partial<T>)) => {\n setFilters(prevFilters => {\n const newFilters =\n typeof update === 'function' ? update(prevFilters) : update;\n return { ...prevFilters, ...newFilters };\n });\n },\n [],\n );\n\n // Memoize the default values since pickers have useEffect triggers on these; naively defaulting\n // below with `?? <X>` breaks referential equality on subsequent updates.\n const defaultValues = useMemo(\n () => ({\n entities: [],\n backendEntities: [],\n queryParameters: {},\n }),\n [],\n );\n\n const resolvedValue: EntityListContextProps<T> = useMemo(\n () => ({\n entities: value?.entities ?? defaultValues.entities,\n backendEntities: value?.backendEntities ?? defaultValues.backendEntities,\n updateFilters: value?.updateFilters ?? updateFilters,\n filters,\n loading: value?.loading ?? false,\n queryParameters: value?.queryParameters ?? defaultValues.queryParameters,\n error: value?.error,\n totalItems:\n value?.totalItems ?? (value?.entities ?? defaultValues.entities).length,\n limit: value?.limit ?? 20,\n offset: value?.offset,\n setLimit: value?.setLimit ?? (() => {}),\n setOffset: value?.setOffset,\n paginationMode: value?.paginationMode ?? 'none',\n }),\n [value, defaultValues, filters, updateFilters],\n );\n\n return (\n <EntityListContext.Provider value={resolvedValue}>\n {children}\n </EntityListContext.Provider>\n );\n}\n"],"names":[],"mappings":";;;AA6BO,SAAS,8BAGd,KAGA,EAAA;AACA,EAAM,MAAA,EAAE,QAAU,EAAA,KAAA,EAAU,GAAA,KAAA,CAAA;AAI5B,EAAM,MAAA,CAAC,SAAS,UAAU,CAAA,GAAI,SAAY,KAAO,EAAA,OAAA,IAAY,EAAQ,CAAA,CAAA;AAErE,EAAA,MAAM,aAAgB,GAAA,WAAA;AAAA,IACpB,CAAC,MAA0D,KAAA;AACzD,MAAA,UAAA,CAAW,CAAe,WAAA,KAAA;AACxB,QAAA,MAAM,aACJ,OAAO,MAAA,KAAW,UAAa,GAAA,MAAA,CAAO,WAAW,CAAI,GAAA,MAAA,CAAA;AACvD,QAAA,OAAO,EAAE,GAAG,WAAa,EAAA,GAAG,UAAW,EAAA,CAAA;AAAA,OACxC,CAAA,CAAA;AAAA,KACH;AAAA,IACA,EAAC;AAAA,GACH,CAAA;AAIA,EAAA,MAAM,aAAgB,GAAA,OAAA;AAAA,IACpB,OAAO;AAAA,MACL,UAAU,EAAC;AAAA,MACX,iBAAiB,EAAC;AAAA,MAClB,iBAAiB,EAAC;AAAA,KACpB,CAAA;AAAA,IACA,EAAC;AAAA,GACH,CAAA;AAEA,EAAA,MAAM,aAA2C,GAAA,OAAA;AAAA,IAC/C,OAAO;AAAA,MACL,QAAA,EAAU,KAAO,EAAA,QAAA,IAAY,aAAc,CAAA,QAAA;AAAA,MAC3C,eAAA,EAAiB,KAAO,EAAA,eAAA,IAAmB,aAAc,CAAA,eAAA;AAAA,MACzD,aAAA,EAAe,OAAO,aAAiB,IAAA,aAAA;AAAA,MACvC,OAAA;AAAA,MACA,OAAA,EAAS,OAAO,OAAW,IAAA,KAAA;AAAA,MAC3B,eAAA,EAAiB,KAAO,EAAA,eAAA,IAAmB,aAAc,CAAA,eAAA;AAAA,MACzD,OAAO,KAAO,EAAA,KAAA;AAAA,MACd,YACE,KAAO,EAAA,UAAA,IAAA,CAAe,KAAO,EAAA,QAAA,IAAY,cAAc,QAAU,EAAA,MAAA;AAAA,MACnE,KAAA,EAAO,OAAO,KAAS,IAAA,EAAA;AAAA,MACvB,QAAQ,KAAO,EAAA,MAAA;AAAA,MACf,QAAA,EAAU,KAAO,EAAA,QAAA,KAAa,MAAM;AAAA,OAAC,CAAA;AAAA,MACrC,WAAW,KAAO,EAAA,SAAA;AAAA,MAClB,cAAA,EAAgB,OAAO,cAAkB,IAAA,MAAA;AAAA,KAC3C,CAAA;AAAA,IACA,CAAC,KAAA,EAAO,aAAe,EAAA,OAAA,EAAS,aAAa,CAAA;AAAA,GAC/C,CAAA;AAEA,EAAA,2CACG,iBAAkB,CAAA,QAAA,EAAlB,EAA2B,KAAA,EAAO,iBAChC,QACH,CAAA,CAAA;AAEJ;;;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backstage/plugin-catalog-react",
3
- "version": "1.12.4-next.0",
3
+ "version": "1.13.0-next.2",
4
4
  "description": "A frontend library that helps other Backstage plugins interact with the catalog",
5
5
  "backstage": {
6
6
  "role": "web-library",
@@ -56,19 +56,19 @@
56
56
  "test": "backstage-cli package test"
57
57
  },
58
58
  "dependencies": {
59
- "@backstage/catalog-client": "^1.6.6",
59
+ "@backstage/catalog-client": "^1.7.0-next.1",
60
60
  "@backstage/catalog-model": "^1.6.0",
61
- "@backstage/core-compat-api": "^0.2.9-next.0",
62
- "@backstage/core-components": "^0.14.10",
63
- "@backstage/core-plugin-api": "^1.9.3",
61
+ "@backstage/core-compat-api": "^0.3.0-next.2",
62
+ "@backstage/core-components": "^0.14.11-next.1",
63
+ "@backstage/core-plugin-api": "^1.9.4-next.0",
64
64
  "@backstage/errors": "^1.2.4",
65
- "@backstage/frontend-plugin-api": "^0.8.0-next.0",
66
- "@backstage/integration-react": "^1.1.30",
65
+ "@backstage/frontend-plugin-api": "^0.8.0-next.2",
66
+ "@backstage/integration-react": "^1.1.31-next.0",
67
67
  "@backstage/plugin-catalog-common": "^1.0.26",
68
68
  "@backstage/plugin-permission-common": "^0.8.1",
69
- "@backstage/plugin-permission-react": "^0.4.25",
69
+ "@backstage/plugin-permission-react": "^0.4.26-next.0",
70
70
  "@backstage/types": "^1.1.1",
71
- "@backstage/version-bridge": "^1.0.8",
71
+ "@backstage/version-bridge": "^1.0.9-next.0",
72
72
  "@material-ui/core": "^4.12.2",
73
73
  "@material-ui/icons": "^4.9.1",
74
74
  "@material-ui/lab": "4.0.0-alpha.61",
@@ -83,15 +83,15 @@
83
83
  "zen-observable": "^0.10.0"
84
84
  },
85
85
  "devDependencies": {
86
- "@backstage/cli": "^0.27.1-next.0",
87
- "@backstage/core-app-api": "^1.14.2",
88
- "@backstage/frontend-test-utils": "^0.2.0-next.0",
86
+ "@backstage/cli": "^0.27.1-next.2",
87
+ "@backstage/core-app-api": "^1.14.3-next.0",
88
+ "@backstage/frontend-test-utils": "^0.2.0-next.2",
89
89
  "@backstage/plugin-catalog-common": "^1.0.26",
90
90
  "@backstage/plugin-scaffolder-common": "^1.5.5",
91
- "@backstage/test-utils": "^1.6.0-next.0",
91
+ "@backstage/test-utils": "^1.6.0-next.1",
92
92
  "@testing-library/dom": "^10.0.0",
93
93
  "@testing-library/jest-dom": "^6.0.0",
94
- "@testing-library/react": "^15.0.0",
94
+ "@testing-library/react": "^16.0.0",
95
95
  "@testing-library/user-event": "^14.0.0",
96
96
  "@types/zen-observable": "^0.8.0",
97
97
  "react-test-renderer": "^16.13.1"