@backstage/plugin-catalog-react 0.6.12-next.0 → 0.6.13

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,70 @@
1
1
  # @backstage/plugin-catalog-react
2
2
 
3
+ ## 0.6.13
4
+
5
+ ### Patch Changes
6
+
7
+ - f7257dff6f: The `<Link />` component now accepts a `noTrack` prop, which prevents the `click` event from being captured by the Analytics API. This can be used if tracking is explicitly not warranted, or in order to use custom link tracking in specific situations.
8
+ - 300f8cdaee: Fix bug: previously the filter would be set to "all" on page load, even if the
9
+ `initiallySelectedFilter` on the `DefaultCatalogPage` was set to something else,
10
+ or a different query parameter was supplied. Now, the prop and query parameters
11
+ control the filter as expected. Additionally, after this change any filters
12
+ which match 0 items will be disabled, and the filter will be reverted to 'all'
13
+ if they're set on page load.
14
+ - 6acc8f7db7: Add caching to the useEntityPermission hook
15
+
16
+ The hook now caches the authorization decision based on the permission + the entity, and returns the cache match value as the default `allowed` value while loading. This helps avoid flicker in UI elements that would be conditionally rendered based on the `allowed` result of this hook.
17
+
18
+ - Updated dependencies
19
+ - @backstage/core-components@0.8.7
20
+
21
+ ## 0.6.13-next.1
22
+
23
+ ### Patch Changes
24
+
25
+ - f7257dff6f: The `<Link />` component now accepts a `noTrack` prop, which prevents the `click` event from being captured by the Analytics API. This can be used if tracking is explicitly not warranted, or in order to use custom link tracking in specific situations.
26
+ - 300f8cdaee: Fix bug: previously the filter would be set to "all" on page load, even if the
27
+ `initiallySelectedFilter` on the `DefaultCatalogPage` was set to something else,
28
+ or a different query parameter was supplied. Now, the prop and query parameters
29
+ control the filter as expected. Additionally, after this change any filters
30
+ which match 0 items will be disabled, and the filter will be reverted to 'all'
31
+ if they're set on page load.
32
+ - 6acc8f7db7: Add caching to the useEntityPermission hook
33
+
34
+ The hook now caches the authorization decision based on the permission + the entity, and returns the cache match value as the default `allowed` value while loading. This helps avoid flicker in UI elements that would be conditionally rendered based on the `allowed` result of this hook.
35
+
36
+ - Updated dependencies
37
+ - @backstage/core-components@0.8.7-next.1
38
+
39
+ ## 0.6.13-next.0
40
+
41
+ ### Patch Changes
42
+
43
+ - Updated dependencies
44
+ - @backstage/core-components@0.8.7-next.0
45
+
46
+ ## 0.6.12
47
+
48
+ ### Patch Changes
49
+
50
+ - 3d87019269: The `entityRouteRef` is now a well-known route that should be imported directly from `@backstage/plugin-catalog-react`. It is guaranteed to be globally unique across duplicate installations of the `@backstage/plugin-catalog-react`, starting at this version.
51
+
52
+ Deprecated `entityRoute` in favor of `entityRouteRef`.
53
+
54
+ Deprecated `rootRoute` and `catalogRouteRef`. If you want to refer to the catalog index page from a public plugin you now need to use an `ExternalRouteRef` instead. For private plugins it is possible to take the shortcut of referring directly to `catalogPlugin.routes.indexPage` instead.
55
+
56
+ - 2916a83b9c: Deprecated `loadIdentityOwnerRefs`, since they can now be retrieved as `ownershipEntityRefs` from `identityApi.getBackstageIdentity()` instead.
57
+ - 51fbedc445: Migrated usage of deprecated `IdentityApi` methods.
58
+ - c54c0d9d10: Add useEntityPermission hook
59
+ - Updated dependencies
60
+ - @backstage/plugin-permission-react@0.3.0
61
+ - @backstage/core-components@0.8.5
62
+ - @backstage/integration@0.7.2
63
+ - @backstage/plugin-permission-common@0.4.0
64
+ - @backstage/core-plugin-api@0.6.0
65
+ - @backstage/catalog-model@0.9.10
66
+ - @backstage/catalog-client@0.5.5
67
+
3
68
  ## 0.6.12-next.0
4
69
 
5
70
  ### Patch Changes
package/dist/index.cjs.js CHANGED
@@ -21,6 +21,7 @@ var isEqual = require('lodash/isEqual');
21
21
  var coreComponents = require('@backstage/core-components');
22
22
  var core = require('@material-ui/core');
23
23
  var useObservable = require('react-use/lib/useObservable');
24
+ var pluginPermissionReact = require('@backstage/plugin-permission-react');
24
25
  var CheckBoxIcon = require('@material-ui/icons/CheckBox');
25
26
  var CheckBoxOutlineBlankIcon = require('@material-ui/icons/CheckBoxOutlineBlank');
26
27
  var ExpandMoreIcon = require('@material-ui/icons/ExpandMore');
@@ -127,11 +128,11 @@ const rootRoute = corePluginApi.createRouteRef({
127
128
  id: "catalog"
128
129
  });
129
130
  const catalogRouteRef = rootRoute;
130
- const entityRoute = corePluginApi.createRouteRef({
131
+ const entityRouteRef = versionBridge.getOrCreateGlobalSingleton("catalog:entity-route-ref", () => corePluginApi.createRouteRef({
131
132
  id: "catalog:entity",
132
133
  params: ["namespace", "kind", "name"]
133
- });
134
- const entityRouteRef = entityRoute;
134
+ }));
135
+ const entityRoute = entityRouteRef;
135
136
  function entityRouteParams(entity) {
136
137
  var _a, _b;
137
138
  return {
@@ -779,6 +780,22 @@ function useOwnedEntities(allowedKinds) {
779
780
  return React.useMemo(() => ({ loading, ownedEntities }), [loading, ownedEntities]);
780
781
  }
781
782
 
783
+ function useEntityPermission(permission) {
784
+ const { entity, loading: loadingEntity, error: entityError } = useEntity();
785
+ const {
786
+ allowed,
787
+ loading: loadingPermission,
788
+ error: permissionError
789
+ } = pluginPermissionReact.usePermission(permission, entity ? catalogModel.stringifyEntityRef(entity) : void 0);
790
+ if (loadingEntity || loadingPermission) {
791
+ return { loading: true, allowed: false };
792
+ }
793
+ if (entityError) {
794
+ return { loading: false, allowed: false, error: entityError };
795
+ }
796
+ return { loading: false, allowed, error: permissionError };
797
+ }
798
+
782
799
  const EntityKindPicker = ({
783
800
  initialFilter,
784
801
  hidden
@@ -1503,7 +1520,7 @@ const UserListPicker = ({
1503
1520
  const classes = useStyles();
1504
1521
  const configApi = corePluginApi.useApi(corePluginApi.configApiRef);
1505
1522
  const orgName = (_a = configApi.getOptionalString("organization.name")) != null ? _a : "Company";
1506
- const { filters, updateFilters, backendEntities, queryParameters } = useEntityListProvider();
1523
+ const { filters, updateFilters, backendEntities, queryParameters, loading } = useEntityListProvider();
1507
1524
  const userAndGroupFilterIds = ["starred", "all"];
1508
1525
  const filterGroups = getFilterGroups(orgName).map((filterGroup) => ({
1509
1526
  ...filterGroup,
@@ -1513,28 +1530,23 @@ const UserListPicker = ({
1513
1530
  const { isOwnedEntity } = useEntityOwnership();
1514
1531
  const ownedFilter = React.useMemo(() => new UserListFilter("owned", isOwnedEntity, isStarredEntity), [isOwnedEntity, isStarredEntity]);
1515
1532
  const starredFilter = React.useMemo(() => new UserListFilter("starred", isOwnedEntity, isStarredEntity), [isOwnedEntity, isStarredEntity]);
1516
- const [entitiesWithoutUserFilter, setEntitiesWithoutUserFilter] = React.useState(backendEntities);
1517
- const totalOwnedUserEntities = entitiesWithoutUserFilter.filter((entity) => ownedFilter.filterEntity(entity)).length;
1518
- const [selectedUserFilter, setSelectedUserFilter] = React.useState(totalOwnedUserEntities > 0 ? (_b = [queryParameters.user].flat()[0]) != null ? _b : initialFilter : "all");
1533
+ const [selectedUserFilter, setSelectedUserFilter] = React.useState((_b = [queryParameters.user].flat()[0]) != null ? _b : initialFilter);
1534
+ const entitiesWithoutUserFilter = React.useMemo(() => backendEntities.filter(reduceEntityFilters(lodash.compact(Object.values({ ...filters, user: void 0 })))), [filters, backendEntities]);
1535
+ const filterCounts = React.useMemo(() => ({
1536
+ all: entitiesWithoutUserFilter.length,
1537
+ starred: entitiesWithoutUserFilter.filter((entity) => starredFilter.filterEntity(entity)).length,
1538
+ owned: entitiesWithoutUserFilter.filter((entity) => ownedFilter.filterEntity(entity)).length
1539
+ }), [entitiesWithoutUserFilter, starredFilter, ownedFilter]);
1540
+ React.useEffect(() => {
1541
+ if (!loading && !!selectedUserFilter && selectedUserFilter !== "all" && filterCounts[selectedUserFilter] === 0) {
1542
+ setSelectedUserFilter("all");
1543
+ }
1544
+ }, [loading, filterCounts, selectedUserFilter, setSelectedUserFilter]);
1519
1545
  React.useEffect(() => {
1520
1546
  updateFilters({
1521
1547
  user: selectedUserFilter ? new UserListFilter(selectedUserFilter, isOwnedEntity, isStarredEntity) : void 0
1522
1548
  });
1523
1549
  }, [selectedUserFilter, isOwnedEntity, isStarredEntity, updateFilters]);
1524
- React.useEffect(() => {
1525
- const filterFn = reduceEntityFilters(lodash.compact(Object.values({ ...filters, user: void 0 })));
1526
- setEntitiesWithoutUserFilter(backendEntities.filter(filterFn));
1527
- }, [filters, backendEntities]);
1528
- function getFilterCount(id) {
1529
- switch (id) {
1530
- case "owned":
1531
- return totalOwnedUserEntities;
1532
- case "starred":
1533
- return entitiesWithoutUserFilter.filter((entity) => starredFilter.filterEntity(entity)).length;
1534
- default:
1535
- return entitiesWithoutUserFilter.length;
1536
- }
1537
- }
1538
1550
  return /* @__PURE__ */ React__default["default"].createElement(core.Card, {
1539
1551
  className: classes.root
1540
1552
  }, filterGroups.map((group) => /* @__PURE__ */ React__default["default"].createElement(React.Fragment, {
@@ -1555,15 +1567,16 @@ const UserListPicker = ({
1555
1567
  divider: true,
1556
1568
  onClick: () => setSelectedUserFilter(item.id),
1557
1569
  selected: item.id === ((_a2 = filters.user) == null ? void 0 : _a2.value),
1558
- className: classes.menuItem
1570
+ className: classes.menuItem,
1571
+ disabled: filterCounts[item.id] === 0,
1572
+ "data-testid": `user-picker-${item.id}`
1559
1573
  }, item.icon && /* @__PURE__ */ React__default["default"].createElement(core.ListItemIcon, {
1560
1574
  className: classes.listIcon
1561
1575
  }, /* @__PURE__ */ React__default["default"].createElement(item.icon, {
1562
1576
  fontSize: "small"
1563
1577
  })), /* @__PURE__ */ React__default["default"].createElement(core.ListItemText, null, /* @__PURE__ */ React__default["default"].createElement(core.Typography, {
1564
- variant: "body1",
1565
- "data-testid": `user-picker-${item.id}`
1566
- }, item.label)), /* @__PURE__ */ React__default["default"].createElement(core.ListItemSecondaryAction, null, (_b2 = getFilterCount(item.id)) != null ? _b2 : "-"));
1578
+ variant: "body1"
1579
+ }, item.label)), /* @__PURE__ */ React__default["default"].createElement(core.ListItemSecondaryAction, null, (_b2 = filterCounts[item.id]) != null ? _b2 : "-"));
1567
1580
  }))))));
1568
1581
  };
1569
1582
 
@@ -1648,6 +1661,7 @@ exports.useEntityFromUrl = useEntityFromUrl;
1648
1661
  exports.useEntityKinds = useEntityKinds;
1649
1662
  exports.useEntityListProvider = useEntityListProvider;
1650
1663
  exports.useEntityOwnership = useEntityOwnership;
1664
+ exports.useEntityPermission = useEntityPermission;
1651
1665
  exports.useEntityTypeFilter = useEntityTypeFilter;
1652
1666
  exports.useOwnUser = useOwnUser;
1653
1667
  exports.useOwnedEntities = useOwnedEntities;