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

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.4
4
+
5
+ ### Patch Changes
6
+
7
+ - ac19f82936: Added ARIA landmark <main> to Page component and added ARIA landmark <nav> to DesktopSidebar and Sidebar components
8
+ - 55f68c386a: Enabled select component to be enabled by keyboard
9
+ - c0055ece91: Announce external links to screen readers
10
+ - e210c0cab8: Add ability to customize `Read More` destination with `readMoreUrl` prop for `MissingAnnotationEmptyState` component.
11
+ - 52c02ac02b: Don't set the background color on an Avatar component that has a picture.
12
+ - cfc0f2e5bd: Added optional anchorOrigin alignment prop to AlertDisplay
13
+ - f4380eb602: Add an aria-label to the support button to improve accessibility for screen readers
14
+ - ba97b80421: Updated dependency `@types/react-syntax-highlighter` to `^15.0.0`.
15
+ - e462112be5: Updated dependency `rc-progress` to `3.3.2`.
16
+ - 2bcb0a0e2b: Sidebar NAV now includes aria-label. Component AboutField now uses h2 variant instead of subtitle2 (font properties unchanged)
17
+ - c7f32b53a4: Fixed multiple scrolls appearing on Page when added InfoCard with external bottom link
18
+ - 3603014e0e: Add ARIA landmark( <main>), & label and a heading to OAuthRequestDialog. Removed nested interactive control (button).
19
+ - 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
20
+ - 2295b4ab2b: Add controls to Storybook stories
21
+ - 521293b22e: Added a chevron Indicator when the sidebar is collapsed and has a sub-menu
22
+ - Updated dependencies
23
+ - @backstage/core-plugin-api@1.0.2
24
+ - @backstage/config@1.0.1
25
+
26
+ ## 0.9.4-next.2
27
+
28
+ ### Patch Changes
29
+
30
+ - 52c02ac02b: Don't set the background color on an Avatar component that has a picture.
31
+ - 3603014e0e: Add ARIA landmark( <main>), & label and a heading to OAuthRequestDialog. Removed nested interactive control (button).
32
+ - 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
33
+
34
+ ## 0.9.4-next.1
35
+
36
+ ### Patch Changes
37
+
38
+ - 55f68c386a: Enabled select component to be enabled by keyboard
39
+ - ba97b80421: Updated dependency `@types/react-syntax-highlighter` to `^15.0.0`.
40
+ - 2bcb0a0e2b: Sidebar NAV now includes aria-label. Component AboutField now uses h2 variant instead of subtitle2 (font properties unchanged)
41
+ - Updated dependencies
42
+ - @backstage/config@1.0.1-next.0
43
+ - @backstage/core-plugin-api@1.0.2-next.1
44
+
3
45
  ## 0.9.4-next.0
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
  /**
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';
@@ -71,6 +71,7 @@ import Checkbox from '@material-ui/core/Checkbox';
71
71
  import Chip from '@material-ui/core/Chip';
72
72
  import FormControl from '@material-ui/core/FormControl';
73
73
  import InputBase from '@material-ui/core/InputBase';
74
+ import InputLabel from '@material-ui/core/InputLabel';
74
75
  import MenuItem from '@material-ui/core/MenuItem';
75
76
  import Select from '@material-ui/core/Select';
76
77
  import SvgIcon from '@material-ui/core/SvgIcon';
@@ -192,14 +193,18 @@ const useStyles$S = makeStyles((theme) => createStyles({
192
193
  function Avatar(props) {
193
194
  const { displayName, picture, customStyles } = props;
194
195
  const classes = useStyles$S();
196
+ let styles = { ...customStyles };
197
+ if (!picture) {
198
+ styles = {
199
+ backgroundColor: stringToColor(displayName || ""),
200
+ ...customStyles
201
+ };
202
+ }
195
203
  return /* @__PURE__ */ React.createElement(MaterialAvatar, {
196
204
  alt: displayName,
197
205
  src: picture,
198
206
  className: classes.avatar,
199
- style: {
200
- backgroundColor: stringToColor(displayName || picture || ""),
201
- ...customStyles
202
- }
207
+ style: styles
203
208
  }, displayName && extractInitials(displayName));
204
209
  }
205
210
 
@@ -212,6 +217,9 @@ const useStyles$R = makeStyles({
212
217
  whiteSpace: "nowrap",
213
218
  height: 1,
214
219
  width: 1
220
+ },
221
+ externalLink: {
222
+ position: "relative"
215
223
  }
216
224
  }, { name: "Link" });
217
225
  const isExternalUri = (uri) => /^([a-z+.-]+):/.test(uri);
@@ -246,7 +254,8 @@ const Link = React.forwardRef(({ onClick, noTrack, ...props }, ref) => {
246
254
  href: to,
247
255
  onClick: handleClick,
248
256
  ...newWindow ? { target: "_blank", rel: "noopener" } : {},
249
- ...props
257
+ ...props,
258
+ className: classNames(classes.externalLink, props.className)
250
259
  }, props.children, /* @__PURE__ */ React.createElement("span", {
251
260
  className: classes.visuallyHidden
252
261
  }, ", Opens in a new window")) : /* @__PURE__ */ React.createElement(Link$1, {
@@ -941,7 +950,8 @@ const useStyles$J = makeStyles((theme) => ({
941
950
  }
942
951
  }), { name: "BackstageMissingAnnotationEmptyState" });
943
952
  function MissingAnnotationEmptyState(props) {
944
- const { annotation } = props;
953
+ const { annotation, readMoreUrl } = props;
954
+ const url = readMoreUrl || "https://backstage.io/docs/features/software-catalog/well-known-annotations";
945
955
  const classes = useStyles$J();
946
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.");
947
957
  return /* @__PURE__ */ React.createElement(EmptyState, {
@@ -961,7 +971,7 @@ function MissingAnnotationEmptyState(props) {
961
971
  })), /* @__PURE__ */ React.createElement(Button$1, {
962
972
  color: "primary",
963
973
  component: Link,
964
- to: "https://backstage.io/docs/features/software-catalog/well-known-annotations"
974
+ to: url
965
975
  }, "Read more"))
966
976
  });
967
977
  }
@@ -1717,7 +1727,6 @@ const LoginRequestListItem = ({ request, busy, setBusy }) => {
1717
1727
  };
1718
1728
  const IconComponent = request.provider.icon;
1719
1729
  return /* @__PURE__ */ React.createElement(ListItem, {
1720
- button: true,
1721
1730
  disabled: busy,
1722
1731
  classes: { root: classes.root }
1723
1732
  }, /* @__PURE__ */ React.createElement(ListItemAvatar, null, /* @__PURE__ */ React.createElement(IconComponent, {
@@ -1741,6 +1750,9 @@ const useStyles$A = makeStyles((theme) => ({
1741
1750
  title: {
1742
1751
  minWidth: 0
1743
1752
  },
1753
+ titleHeading: {
1754
+ fontSize: theme.typography.h6.fontSize
1755
+ },
1744
1756
  contentList: {
1745
1757
  padding: 0
1746
1758
  },
@@ -1760,10 +1772,15 @@ function OAuthRequestDialog(_props) {
1760
1772
  open: Boolean(requests.length),
1761
1773
  fullWidth: true,
1762
1774
  maxWidth: "xs",
1763
- classes: { paper: classes.dialog }
1764
- }, /* @__PURE__ */ React.createElement(DialogTitle, {
1765
- classes: { root: classes.title }
1766
- }, "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, {
1767
1784
  dividers: true,
1768
1785
  classes: { root: classes.contentList }
1769
1786
  }, /* @__PURE__ */ React.createElement(List, null, requests.map((request) => /* @__PURE__ */ React.createElement(LoginRequestListItem, {
@@ -1771,7 +1788,7 @@ function OAuthRequestDialog(_props) {
1771
1788
  request,
1772
1789
  busy,
1773
1790
  setBusy
1774
- })))), /* @__PURE__ */ React.createElement(DialogActions, {
1791
+ }))))), /* @__PURE__ */ React.createElement(DialogActions, {
1775
1792
  classes: { root: classes.actionButtons }
1776
1793
  }, /* @__PURE__ */ React.createElement(Button$1, {
1777
1794
  onClick: handleRejectAll
@@ -2264,6 +2281,16 @@ const useStyles$s = makeStyles((theme) => createStyles({
2264
2281
  color: theme.palette.text.primary
2265
2282
  }
2266
2283
  },
2284
+ formLabel: {
2285
+ transform: "initial",
2286
+ fontWeight: "bold",
2287
+ fontSize: 14,
2288
+ fontFamily: theme.typography.fontFamily,
2289
+ color: theme.palette.text.primary,
2290
+ "&.Mui-focused": {
2291
+ color: theme.palette.text.primary
2292
+ }
2293
+ },
2267
2294
  chips: {
2268
2295
  display: "flex",
2269
2296
  flexWrap: "wrap"
@@ -2326,13 +2353,13 @@ function SelectComponent(props) {
2326
2353
  };
2327
2354
  return /* @__PURE__ */ React.createElement("div", {
2328
2355
  className: classes.root
2329
- }, /* @__PURE__ */ React.createElement(Typography, {
2330
- variant: "button"
2331
- }, label), /* @__PURE__ */ React.createElement(ClickAwayListener, {
2356
+ }, /* @__PURE__ */ React.createElement(ClickAwayListener, {
2332
2357
  onClickAway: handleClickAway
2333
2358
  }, /* @__PURE__ */ React.createElement(FormControl, {
2334
2359
  className: classes.formControl
2335
- }, /* @__PURE__ */ React.createElement(Select, {
2360
+ }, /* @__PURE__ */ React.createElement(InputLabel, {
2361
+ className: classes.formLabel
2362
+ }, label), /* @__PURE__ */ React.createElement(Select, {
2336
2363
  value,
2337
2364
  native,
2338
2365
  disabled,
@@ -2343,6 +2370,8 @@ function SelectComponent(props) {
2343
2370
  onClick: handleClick,
2344
2371
  open: isOpen,
2345
2372
  input: /* @__PURE__ */ React.createElement(BootstrapInput, null),
2373
+ label,
2374
+ tabIndex: 0,
2346
2375
  renderValue: (s) => {
2347
2376
  var _a;
2348
2377
  return multiple && value.length !== 0 ? /* @__PURE__ */ React.createElement("div", {
@@ -3056,17 +3085,17 @@ const LocalStorage = {
3056
3085
  };
3057
3086
 
3058
3087
  const useStyles$n = makeStyles((theme) => ({
3059
- root: (props) => ({
3088
+ root: {
3060
3089
  width: "100%",
3061
3090
  transition: "padding-left 0.1s ease-out",
3062
3091
  isolation: "isolate",
3063
3092
  [theme.breakpoints.up("sm")]: {
3064
- paddingLeft: () => props.isPinned ? props.sidebarConfig.drawerWidthOpen : props.sidebarConfig.drawerWidthClosed
3093
+ paddingLeft: (props) => props.isPinned ? props.sidebarConfig.drawerWidthOpen : props.sidebarConfig.drawerWidthClosed
3065
3094
  },
3066
3095
  [theme.breakpoints.down("xs")]: {
3067
- paddingBottom: props.sidebarConfig.mobileSidebarHeight
3096
+ paddingBottom: (props) => props.sidebarConfig.mobileSidebarHeight
3068
3097
  }
3069
- }),
3098
+ },
3070
3099
  content: {
3071
3100
  zIndex: 0,
3072
3101
  isolation: "isolate",
@@ -3380,7 +3409,8 @@ const DesktopSidebar = (props) => {
3380
3409
  }
3381
3410
  };
3382
3411
  return /* @__PURE__ */ React.createElement("nav", {
3383
- style: {}
3412
+ style: {},
3413
+ "aria-label": "sidebar nav"
3384
3414
  }, /* @__PURE__ */ React.createElement(A11ySkipSidebar, null), /* @__PURE__ */ React.createElement(SidebarContext.Provider, {
3385
3415
  value: { isOpen, setOpen }
3386
3416
  }, /* @__PURE__ */ React.createElement("div", {
@@ -3464,7 +3494,10 @@ const useStyles$j = makeStyles((theme) => ({
3464
3494
  label: {
3465
3495
  margin: 14,
3466
3496
  marginLeft: 7,
3467
- fontSize: 14
3497
+ fontSize: 14,
3498
+ whiteSpace: "nowrap",
3499
+ overflow: "hidden",
3500
+ "text-overflow": "ellipsis"
3468
3501
  },
3469
3502
  dropdownArrow: {
3470
3503
  position: "absolute",
@@ -3477,17 +3510,20 @@ const useStyles$j = makeStyles((theme) => ({
3477
3510
  },
3478
3511
  dropdownItem: {
3479
3512
  width: "100%",
3480
- padding: "10px 0 10px 0"
3513
+ padding: "10px 0 10px 0",
3514
+ "&:hover": {
3515
+ background: "#6f6f6f",
3516
+ color: theme.palette.navigation.selectedColor
3517
+ }
3481
3518
  },
3482
3519
  textContent: {
3483
3520
  color: theme.palette.navigation.color,
3484
- display: "flex",
3485
- justifyContent: "center",
3486
- [theme.breakpoints.down("xs")]: {
3487
- display: "block",
3488
- paddingLeft: theme.spacing(4)
3489
- },
3490
- fontSize: "14px"
3521
+ paddingLeft: theme.spacing(4),
3522
+ paddingRight: theme.spacing(1),
3523
+ fontSize: "14px",
3524
+ whiteSpace: "nowrap",
3525
+ overflow: "hidden",
3526
+ "text-overflow": "ellipsis"
3491
3527
  }
3492
3528
  }), { name: "BackstageSidebarSubmenuItem" });
3493
3529
  const SidebarSubmenuItem = (props) => {
@@ -3497,7 +3533,7 @@ const SidebarSubmenuItem = (props) => {
3497
3533
  const closeSubmenu = () => {
3498
3534
  setIsHoveredOn(false);
3499
3535
  };
3500
- const toLocation = useResolvedPath(to);
3536
+ const toLocation = useResolvedPath(to != null ? to : "");
3501
3537
  const currentLocation = useLocation();
3502
3538
  let isActive = isLocationMatch(currentLocation, toLocation);
3503
3539
  const [showDropDown, setShowDropDown] = useState(false);
@@ -3512,11 +3548,15 @@ const SidebarSubmenuItem = (props) => {
3512
3548
  });
3513
3549
  return /* @__PURE__ */ React.createElement("div", {
3514
3550
  className: classes.itemContainer
3551
+ }, /* @__PURE__ */ React.createElement(Tooltip, {
3552
+ title,
3553
+ enterDelay: 500,
3554
+ enterNextDelay: 500
3515
3555
  }, /* @__PURE__ */ React.createElement("button", {
3516
3556
  onClick: handleClickDropdown,
3517
3557
  onTouchStart: (e) => e.stopPropagation(),
3518
3558
  className: classNames(classes.item, isActive ? classes.selected : void 0)
3519
- }, /* @__PURE__ */ React.createElement(Icon, {
3559
+ }, Icon && /* @__PURE__ */ React.createElement(Icon, {
3520
3560
  fontSize: "small"
3521
3561
  }), /* @__PURE__ */ React.createElement(Typography, {
3522
3562
  variant: "subtitle1",
@@ -3525,33 +3565,41 @@ const SidebarSubmenuItem = (props) => {
3525
3565
  className: classes.dropdownArrow
3526
3566
  }) : /* @__PURE__ */ React.createElement(ArrowDropDownIcon, {
3527
3567
  className: classes.dropdownArrow
3528
- })), dropdownItems && showDropDown && /* @__PURE__ */ React.createElement("div", {
3568
+ }))), dropdownItems && showDropDown && /* @__PURE__ */ React.createElement("div", {
3529
3569
  className: classes.dropdown
3530
- }, dropdownItems.map((object, key) => /* @__PURE__ */ React.createElement(Link, {
3570
+ }, dropdownItems.map((object, key) => /* @__PURE__ */ React.createElement(Tooltip, {
3571
+ key,
3572
+ title: object.title,
3573
+ enterDelay: 500,
3574
+ enterNextDelay: 500
3575
+ }, /* @__PURE__ */ React.createElement(Link, {
3531
3576
  to: object.to,
3532
3577
  underline: "none",
3533
3578
  className: classes.dropdownItem,
3534
3579
  onClick: closeSubmenu,
3535
- onTouchStart: (e) => e.stopPropagation(),
3536
- key
3580
+ onTouchStart: (e) => e.stopPropagation()
3537
3581
  }, /* @__PURE__ */ React.createElement(Typography, {
3538
3582
  className: classes.textContent
3539
- }, object.title)))));
3583
+ }, object.title))))));
3540
3584
  }
3541
3585
  return /* @__PURE__ */ React.createElement("div", {
3542
3586
  className: classes.itemContainer
3587
+ }, /* @__PURE__ */ React.createElement(Tooltip, {
3588
+ title,
3589
+ enterDelay: 500,
3590
+ enterNextDelay: 500
3543
3591
  }, /* @__PURE__ */ React.createElement(Link, {
3544
3592
  to,
3545
3593
  underline: "none",
3546
3594
  className: classNames(classes.item, isActive ? classes.selected : void 0),
3547
3595
  onClick: closeSubmenu,
3548
3596
  onTouchStart: (e) => e.stopPropagation()
3549
- }, /* @__PURE__ */ React.createElement(Icon, {
3597
+ }, Icon && /* @__PURE__ */ React.createElement(Icon, {
3550
3598
  fontSize: "small"
3551
3599
  }), /* @__PURE__ */ React.createElement(Typography, {
3552
3600
  variant: "subtitle1",
3553
3601
  className: classes.label
3554
- }, title)));
3602
+ }, title))));
3555
3603
  };
3556
3604
 
3557
3605
  const useStyles$i = makeStyles((theme) => ({
@@ -3865,15 +3913,19 @@ const SidebarItemBase = forwardRef((props, ref) => {
3865
3913
  const { sidebarConfig } = useContext(SidebarConfigContext);
3866
3914
  const classes = useMemoStyles(sidebarConfig);
3867
3915
  const { isOpen } = useContext(SidebarContext);
3916
+ const divStyle = !isOpen && children ? { display: "flex", marginLeft: "24px" } : {};
3917
+ const displayItemIcon = /* @__PURE__ */ React.createElement("div", {
3918
+ style: divStyle
3919
+ }, /* @__PURE__ */ React.createElement(Icon, {
3920
+ fontSize: "small"
3921
+ }), !isOpen && children ? /* @__PURE__ */ React.createElement(ArrowRightIcon, null) : /* @__PURE__ */ React.createElement(React.Fragment, null));
3868
3922
  const itemIcon = /* @__PURE__ */ React.createElement(Badge, {
3869
3923
  color: "secondary",
3870
3924
  variant: "dot",
3871
3925
  overlap: "circular",
3872
3926
  invisible: !hasNotifications,
3873
3927
  className: classNames({ [classes.closedItemIcon]: !isOpen })
3874
- }, /* @__PURE__ */ React.createElement(Icon, {
3875
- fontSize: "small"
3876
- }));
3928
+ }, displayItemIcon);
3877
3929
  const openContent = /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", {
3878
3930
  "data-testid": "login-button",
3879
3931
  className: classes.iconContainer