@backstage/core-components 0.9.4-next.1 → 0.9.5-next.0

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,47 @@
1
1
  # @backstage/core-components
2
2
 
3
+ ## 0.9.5-next.0
4
+
5
+ ### Patch Changes
6
+
7
+ - 65840b17be: Fix issue where right arrow icon was incorrectly added to side bar items without a sub-menu
8
+ - 6968b65ba1: Updated dependency `@react-hookz/web` to `^14.0.0`.
9
+ - 96d1e01641: Accessibility updates:
10
+
11
+ - Added `aria-label` to the `Select` component
12
+ - Changed heading level used in the header of `Table` component
13
+
14
+ ## 0.9.4
15
+
16
+ ### Patch Changes
17
+
18
+ - ac19f82936: Added ARIA landmark <main> to Page component and added ARIA landmark <nav> to DesktopSidebar and Sidebar components
19
+ - 55f68c386a: Enabled select component to be enabled by keyboard
20
+ - c0055ece91: Announce external links to screen readers
21
+ - e210c0cab8: Add ability to customize `Read More` destination with `readMoreUrl` prop for `MissingAnnotationEmptyState` component.
22
+ - 52c02ac02b: Don't set the background color on an Avatar component that has a picture.
23
+ - cfc0f2e5bd: Added optional anchorOrigin alignment prop to AlertDisplay
24
+ - f4380eb602: Add an aria-label to the support button to improve accessibility for screen readers
25
+ - ba97b80421: Updated dependency `@types/react-syntax-highlighter` to `^15.0.0`.
26
+ - e462112be5: Updated dependency `rc-progress` to `3.3.2`.
27
+ - 2bcb0a0e2b: Sidebar NAV now includes aria-label. Component AboutField now uses h2 variant instead of subtitle2 (font properties unchanged)
28
+ - c7f32b53a4: Fixed multiple scrolls appearing on Page when added InfoCard with external bottom link
29
+ - 3603014e0e: Add ARIA landmark( <main>), & label and a heading to OAuthRequestDialog. Removed nested interactive control (button).
30
+ - 2025d7c123: Properly highlight `SidebarSubmenuItem` dropdown items on hover, use ellipsis styling on long labels in `SidebarSubmenu`, allow `icon` and `to` properties to be optional on `SidebarSubmenuItem`, and fix `SidebarPage` padding to be responsive to pinned state
31
+ - 2295b4ab2b: Add controls to Storybook stories
32
+ - 521293b22e: Added a chevron Indicator when the sidebar is collapsed and has a sub-menu
33
+ - Updated dependencies
34
+ - @backstage/core-plugin-api@1.0.2
35
+ - @backstage/config@1.0.1
36
+
37
+ ## 0.9.4-next.2
38
+
39
+ ### Patch Changes
40
+
41
+ - 52c02ac02b: Don't set the background color on an Avatar component that has a picture.
42
+ - 3603014e0e: Add ARIA landmark( <main>), & label and a heading to OAuthRequestDialog. Removed nested interactive control (button).
43
+ - 2025d7c123: Properly highlight `SidebarSubmenuItem` dropdown items on hover, use ellipsis styling on long labels in `SidebarSubmenu`, allow `icon` and `to` properties to be optional on `SidebarSubmenuItem`, and fix `SidebarPage` padding to be responsive to pinned state
44
+
3
45
  ## 0.9.4-next.1
4
46
 
5
47
  ### Patch Changes
package/dist/index.d.ts CHANGED
@@ -553,6 +553,7 @@ declare type EmptyStateImageClassKey = 'generalImg';
553
553
 
554
554
  declare type Props$g = {
555
555
  annotation: string;
556
+ readMoreUrl?: string;
556
557
  };
557
558
  declare type MissingAnnotationEmptyStateClassKey = 'code';
558
559
  declare function MissingAnnotationEmptyState(props: Props$g): JSX.Element;
@@ -1522,8 +1523,8 @@ declare type SidebarSubmenuItemDropdownItem = {
1522
1523
  */
1523
1524
  declare type SidebarSubmenuItemProps = {
1524
1525
  title: string;
1525
- to: string;
1526
- icon: IconComponent;
1526
+ to?: string;
1527
+ icon?: IconComponent;
1527
1528
  dropdownItems?: SidebarSubmenuItemDropdownItem[];
1528
1529
  };
1529
1530
  /**
@@ -1611,6 +1612,7 @@ declare type SidebarItemBaseProps = {
1611
1612
  icon: IconComponent;
1612
1613
  text?: string;
1613
1614
  hasNotifications?: boolean;
1615
+ hasSubmenu?: boolean;
1614
1616
  disableHighlight?: boolean;
1615
1617
  className?: string;
1616
1618
  };
package/dist/index.esm.js CHANGED
@@ -8,6 +8,7 @@ import pluralize from 'pluralize';
8
8
  import { makeStyles, createStyles, useTheme, darken, lighten, withStyles, styled, ThemeProvider } from '@material-ui/core/styles';
9
9
  import MaterialAvatar from '@material-ui/core/Avatar';
10
10
  import Button$1 from '@material-ui/core/Button';
11
+ import classNames from 'classnames';
11
12
  import Link$1 from '@material-ui/core/Link';
12
13
  import { Link as Link$2, useSearchParams, useLocation, useResolvedPath, resolvePath } from 'react-router-dom';
13
14
  import Tooltip from '@material-ui/core/Tooltip';
@@ -27,7 +28,6 @@ import makeStyles$1 from '@material-ui/core/styles/makeStyles';
27
28
  import * as d3Shape from 'd3-shape';
28
29
  import isFinite from 'lodash/isFinite';
29
30
  import useObservable from 'react-use/lib/useObservable';
30
- import classNames from 'classnames';
31
31
  import SnackbarContent from '@material-ui/core/SnackbarContent';
32
32
  import Grid from '@material-ui/core/Grid';
33
33
  import Typography from '@material-ui/core/Typography';
@@ -193,14 +193,18 @@ const useStyles$S = makeStyles((theme) => createStyles({
193
193
  function Avatar(props) {
194
194
  const { displayName, picture, customStyles } = props;
195
195
  const classes = useStyles$S();
196
+ let styles = { ...customStyles };
197
+ if (!picture) {
198
+ styles = {
199
+ backgroundColor: stringToColor(displayName || ""),
200
+ ...customStyles
201
+ };
202
+ }
196
203
  return /* @__PURE__ */ React.createElement(MaterialAvatar, {
197
204
  alt: displayName,
198
205
  src: picture,
199
206
  className: classes.avatar,
200
- style: {
201
- backgroundColor: stringToColor(displayName || picture || ""),
202
- ...customStyles
203
- }
207
+ style: styles
204
208
  }, displayName && extractInitials(displayName));
205
209
  }
206
210
 
@@ -213,6 +217,9 @@ const useStyles$R = makeStyles({
213
217
  whiteSpace: "nowrap",
214
218
  height: 1,
215
219
  width: 1
220
+ },
221
+ externalLink: {
222
+ position: "relative"
216
223
  }
217
224
  }, { name: "Link" });
218
225
  const isExternalUri = (uri) => /^([a-z+.-]+):/.test(uri);
@@ -247,7 +254,8 @@ const Link = React.forwardRef(({ onClick, noTrack, ...props }, ref) => {
247
254
  href: to,
248
255
  onClick: handleClick,
249
256
  ...newWindow ? { target: "_blank", rel: "noopener" } : {},
250
- ...props
257
+ ...props,
258
+ className: classNames(classes.externalLink, props.className)
251
259
  }, props.children, /* @__PURE__ */ React.createElement("span", {
252
260
  className: classes.visuallyHidden
253
261
  }, ", Opens in a new window")) : /* @__PURE__ */ React.createElement(Link$1, {
@@ -942,7 +950,8 @@ const useStyles$J = makeStyles((theme) => ({
942
950
  }
943
951
  }), { name: "BackstageMissingAnnotationEmptyState" });
944
952
  function MissingAnnotationEmptyState(props) {
945
- const { annotation } = props;
953
+ const { annotation, readMoreUrl } = props;
954
+ const url = readMoreUrl || "https://backstage.io/docs/features/software-catalog/well-known-annotations";
946
955
  const classes = useStyles$J();
947
956
  const description = /* @__PURE__ */ React.createElement(React.Fragment, null, "The ", /* @__PURE__ */ React.createElement("code", null, annotation), " annotation is missing. You need to add the annotation to your component if you want to enable this tool.");
948
957
  return /* @__PURE__ */ React.createElement(EmptyState, {
@@ -962,7 +971,7 @@ function MissingAnnotationEmptyState(props) {
962
971
  })), /* @__PURE__ */ React.createElement(Button$1, {
963
972
  color: "primary",
964
973
  component: Link,
965
- to: "https://backstage.io/docs/features/software-catalog/well-known-annotations"
974
+ to: url
966
975
  }, "Read more"))
967
976
  });
968
977
  }
@@ -1718,7 +1727,6 @@ const LoginRequestListItem = ({ request, busy, setBusy }) => {
1718
1727
  };
1719
1728
  const IconComponent = request.provider.icon;
1720
1729
  return /* @__PURE__ */ React.createElement(ListItem, {
1721
- button: true,
1722
1730
  disabled: busy,
1723
1731
  classes: { root: classes.root }
1724
1732
  }, /* @__PURE__ */ React.createElement(ListItemAvatar, null, /* @__PURE__ */ React.createElement(IconComponent, {
@@ -1742,6 +1750,9 @@ const useStyles$A = makeStyles((theme) => ({
1742
1750
  title: {
1743
1751
  minWidth: 0
1744
1752
  },
1753
+ titleHeading: {
1754
+ fontSize: theme.typography.h6.fontSize
1755
+ },
1745
1756
  contentList: {
1746
1757
  padding: 0
1747
1758
  },
@@ -1761,10 +1772,15 @@ function OAuthRequestDialog(_props) {
1761
1772
  open: Boolean(requests.length),
1762
1773
  fullWidth: true,
1763
1774
  maxWidth: "xs",
1764
- classes: { paper: classes.dialog }
1765
- }, /* @__PURE__ */ React.createElement(DialogTitle, {
1766
- classes: { root: classes.title }
1767
- }, "Login Required"), /* @__PURE__ */ React.createElement(DialogContent, {
1775
+ classes: { paper: classes.dialog },
1776
+ "aria-labelledby": "oauth-req-dialog-title"
1777
+ }, /* @__PURE__ */ React.createElement("main", null, /* @__PURE__ */ React.createElement(DialogTitle, {
1778
+ classes: { root: classes.title },
1779
+ id: "oauth-req-dialog-title"
1780
+ }, /* @__PURE__ */ React.createElement(Typography, {
1781
+ className: classes.titleHeading,
1782
+ variant: "h1"
1783
+ }, "Login Required")), /* @__PURE__ */ React.createElement(DialogContent, {
1768
1784
  dividers: true,
1769
1785
  classes: { root: classes.contentList }
1770
1786
  }, /* @__PURE__ */ React.createElement(List, null, requests.map((request) => /* @__PURE__ */ React.createElement(LoginRequestListItem, {
@@ -1772,7 +1788,7 @@ function OAuthRequestDialog(_props) {
1772
1788
  request,
1773
1789
  busy,
1774
1790
  setBusy
1775
- })))), /* @__PURE__ */ React.createElement(DialogActions, {
1791
+ }))))), /* @__PURE__ */ React.createElement(DialogActions, {
1776
1792
  classes: { root: classes.actionButtons }
1777
1793
  }, /* @__PURE__ */ React.createElement(Button$1, {
1778
1794
  onClick: handleRejectAll
@@ -2344,6 +2360,7 @@ function SelectComponent(props) {
2344
2360
  }, /* @__PURE__ */ React.createElement(InputLabel, {
2345
2361
  className: classes.formLabel
2346
2362
  }, label), /* @__PURE__ */ React.createElement(Select, {
2363
+ "aria-label": label,
2347
2364
  value,
2348
2365
  native,
2349
2366
  disabled,
@@ -3069,17 +3086,17 @@ const LocalStorage = {
3069
3086
  };
3070
3087
 
3071
3088
  const useStyles$n = makeStyles((theme) => ({
3072
- root: (props) => ({
3089
+ root: {
3073
3090
  width: "100%",
3074
3091
  transition: "padding-left 0.1s ease-out",
3075
3092
  isolation: "isolate",
3076
3093
  [theme.breakpoints.up("sm")]: {
3077
- paddingLeft: () => props.isPinned ? props.sidebarConfig.drawerWidthOpen : props.sidebarConfig.drawerWidthClosed
3094
+ paddingLeft: (props) => props.isPinned ? props.sidebarConfig.drawerWidthOpen : props.sidebarConfig.drawerWidthClosed
3078
3095
  },
3079
3096
  [theme.breakpoints.down("xs")]: {
3080
- paddingBottom: props.sidebarConfig.mobileSidebarHeight
3097
+ paddingBottom: (props) => props.sidebarConfig.mobileSidebarHeight
3081
3098
  }
3082
- }),
3099
+ },
3083
3100
  content: {
3084
3101
  zIndex: 0,
3085
3102
  isolation: "isolate",
@@ -3478,7 +3495,10 @@ const useStyles$j = makeStyles((theme) => ({
3478
3495
  label: {
3479
3496
  margin: 14,
3480
3497
  marginLeft: 7,
3481
- fontSize: 14
3498
+ fontSize: 14,
3499
+ whiteSpace: "nowrap",
3500
+ overflow: "hidden",
3501
+ "text-overflow": "ellipsis"
3482
3502
  },
3483
3503
  dropdownArrow: {
3484
3504
  position: "absolute",
@@ -3491,17 +3511,20 @@ const useStyles$j = makeStyles((theme) => ({
3491
3511
  },
3492
3512
  dropdownItem: {
3493
3513
  width: "100%",
3494
- padding: "10px 0 10px 0"
3514
+ padding: "10px 0 10px 0",
3515
+ "&:hover": {
3516
+ background: "#6f6f6f",
3517
+ color: theme.palette.navigation.selectedColor
3518
+ }
3495
3519
  },
3496
3520
  textContent: {
3497
3521
  color: theme.palette.navigation.color,
3498
- display: "flex",
3499
- justifyContent: "center",
3500
- [theme.breakpoints.down("xs")]: {
3501
- display: "block",
3502
- paddingLeft: theme.spacing(4)
3503
- },
3504
- fontSize: "14px"
3522
+ paddingLeft: theme.spacing(4),
3523
+ paddingRight: theme.spacing(1),
3524
+ fontSize: "14px",
3525
+ whiteSpace: "nowrap",
3526
+ overflow: "hidden",
3527
+ "text-overflow": "ellipsis"
3505
3528
  }
3506
3529
  }), { name: "BackstageSidebarSubmenuItem" });
3507
3530
  const SidebarSubmenuItem = (props) => {
@@ -3511,7 +3534,7 @@ const SidebarSubmenuItem = (props) => {
3511
3534
  const closeSubmenu = () => {
3512
3535
  setIsHoveredOn(false);
3513
3536
  };
3514
- const toLocation = useResolvedPath(to);
3537
+ const toLocation = useResolvedPath(to != null ? to : "");
3515
3538
  const currentLocation = useLocation();
3516
3539
  let isActive = isLocationMatch(currentLocation, toLocation);
3517
3540
  const [showDropDown, setShowDropDown] = useState(false);
@@ -3526,11 +3549,15 @@ const SidebarSubmenuItem = (props) => {
3526
3549
  });
3527
3550
  return /* @__PURE__ */ React.createElement("div", {
3528
3551
  className: classes.itemContainer
3552
+ }, /* @__PURE__ */ React.createElement(Tooltip, {
3553
+ title,
3554
+ enterDelay: 500,
3555
+ enterNextDelay: 500
3529
3556
  }, /* @__PURE__ */ React.createElement("button", {
3530
3557
  onClick: handleClickDropdown,
3531
3558
  onTouchStart: (e) => e.stopPropagation(),
3532
3559
  className: classNames(classes.item, isActive ? classes.selected : void 0)
3533
- }, /* @__PURE__ */ React.createElement(Icon, {
3560
+ }, Icon && /* @__PURE__ */ React.createElement(Icon, {
3534
3561
  fontSize: "small"
3535
3562
  }), /* @__PURE__ */ React.createElement(Typography, {
3536
3563
  variant: "subtitle1",
@@ -3539,33 +3566,41 @@ const SidebarSubmenuItem = (props) => {
3539
3566
  className: classes.dropdownArrow
3540
3567
  }) : /* @__PURE__ */ React.createElement(ArrowDropDownIcon, {
3541
3568
  className: classes.dropdownArrow
3542
- })), dropdownItems && showDropDown && /* @__PURE__ */ React.createElement("div", {
3569
+ }))), dropdownItems && showDropDown && /* @__PURE__ */ React.createElement("div", {
3543
3570
  className: classes.dropdown
3544
- }, dropdownItems.map((object, key) => /* @__PURE__ */ React.createElement(Link, {
3571
+ }, dropdownItems.map((object, key) => /* @__PURE__ */ React.createElement(Tooltip, {
3572
+ key,
3573
+ title: object.title,
3574
+ enterDelay: 500,
3575
+ enterNextDelay: 500
3576
+ }, /* @__PURE__ */ React.createElement(Link, {
3545
3577
  to: object.to,
3546
3578
  underline: "none",
3547
3579
  className: classes.dropdownItem,
3548
3580
  onClick: closeSubmenu,
3549
- onTouchStart: (e) => e.stopPropagation(),
3550
- key
3581
+ onTouchStart: (e) => e.stopPropagation()
3551
3582
  }, /* @__PURE__ */ React.createElement(Typography, {
3552
3583
  className: classes.textContent
3553
- }, object.title)))));
3584
+ }, object.title))))));
3554
3585
  }
3555
3586
  return /* @__PURE__ */ React.createElement("div", {
3556
3587
  className: classes.itemContainer
3588
+ }, /* @__PURE__ */ React.createElement(Tooltip, {
3589
+ title,
3590
+ enterDelay: 500,
3591
+ enterNextDelay: 500
3557
3592
  }, /* @__PURE__ */ React.createElement(Link, {
3558
3593
  to,
3559
3594
  underline: "none",
3560
3595
  className: classNames(classes.item, isActive ? classes.selected : void 0),
3561
3596
  onClick: closeSubmenu,
3562
3597
  onTouchStart: (e) => e.stopPropagation()
3563
- }, /* @__PURE__ */ React.createElement(Icon, {
3598
+ }, Icon && /* @__PURE__ */ React.createElement(Icon, {
3564
3599
  fontSize: "small"
3565
3600
  }), /* @__PURE__ */ React.createElement(Typography, {
3566
3601
  variant: "subtitle1",
3567
3602
  className: classes.label
3568
- }, title)));
3603
+ }, title))));
3569
3604
  };
3570
3605
 
3571
3606
  const useStyles$i = makeStyles((theme) => ({
@@ -3870,6 +3905,7 @@ const SidebarItemBase = forwardRef((props, ref) => {
3870
3905
  icon: Icon,
3871
3906
  text,
3872
3907
  hasNotifications = false,
3908
+ hasSubmenu = false,
3873
3909
  disableHighlight = false,
3874
3910
  onClick,
3875
3911
  children,
@@ -3879,15 +3915,19 @@ const SidebarItemBase = forwardRef((props, ref) => {
3879
3915
  const { sidebarConfig } = useContext(SidebarConfigContext);
3880
3916
  const classes = useMemoStyles(sidebarConfig);
3881
3917
  const { isOpen } = useContext(SidebarContext);
3918
+ const divStyle = !isOpen && hasSubmenu ? { display: "flex", marginLeft: "24px" } : {};
3919
+ const displayItemIcon = /* @__PURE__ */ React.createElement("div", {
3920
+ style: divStyle
3921
+ }, /* @__PURE__ */ React.createElement(Icon, {
3922
+ fontSize: "small"
3923
+ }), !isOpen && hasSubmenu ? /* @__PURE__ */ React.createElement(ArrowRightIcon, null) : /* @__PURE__ */ React.createElement(React.Fragment, null));
3882
3924
  const itemIcon = /* @__PURE__ */ React.createElement(Badge, {
3883
3925
  color: "secondary",
3884
3926
  variant: "dot",
3885
3927
  overlap: "circular",
3886
3928
  invisible: !hasNotifications,
3887
3929
  className: classNames({ [classes.closedItemIcon]: !isOpen })
3888
- }, /* @__PURE__ */ React.createElement(Icon, {
3889
- fontSize: "small"
3890
- }));
3930
+ }, displayItemIcon);
3891
3931
  const openContent = /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", {
3892
3932
  "data-testid": "login-button",
3893
3933
  className: classes.iconContainer
@@ -3961,6 +4001,7 @@ const SidebarItemWithSubmenu = ({
3961
4001
  onMouseEnter: handleMouseEnter,
3962
4002
  className: classNames(isHoveredOn && classes.highlighted)
3963
4003
  }, /* @__PURE__ */ React.createElement(SidebarItemBase, {
4004
+ hasSubmenu: true,
3964
4005
  className: isActive ? classes.selected : "",
3965
4006
  ...props
3966
4007
  }, arrowIcon()), isHoveredOn && children));
@@ -4785,7 +4826,7 @@ function Table(props) {
4785
4826
  icons: tableIcons,
4786
4827
  title: /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Typography, {
4787
4828
  variant: "h5",
4788
- component: "h3"
4829
+ component: "h2"
4789
4830
  }, title), subtitle && /* @__PURE__ */ React.createElement(Typography, {
4790
4831
  color: "textSecondary",
4791
4832
  variant: "body1"