@backstage/core-components 0.18.6 → 0.18.7-next.1

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,12 +1,25 @@
1
1
  # @backstage/core-components
2
2
 
3
- ## 0.18.6
3
+ ## 0.18.7-next.1
4
4
 
5
5
  ### Patch Changes
6
6
 
7
- - 5683c85: Bump to latest zod to ensure it has the latest features
8
7
  - Updated dependencies
9
- - @backstage/core-plugin-api@1.12.2
8
+ - @backstage/theme@0.7.2-next.0
9
+
10
+ ## 0.18.6-next.0
11
+
12
+ ### Patch Changes
13
+
14
+ - 7455dae: Use node prefix on native imports
15
+ - cebfea7: Removed link styles from LinkButton to avoid styling inconsistencies related to import order.
16
+ - 69d880e: Bump to latest zod to ensure it has the latest features
17
+ - Updated dependencies
18
+ - @backstage/core-plugin-api@1.12.2-next.0
19
+ - @backstage/config@1.3.6
20
+ - @backstage/errors@1.2.7
21
+ - @backstage/theme@0.7.1
22
+ - @backstage/version-bridge@1.0.11
10
23
 
11
24
  ## 0.18.5
12
25
 
@@ -96,7 +96,7 @@ const getNodeText = (node) => {
96
96
  }
97
97
  return "";
98
98
  };
99
- const Link = forwardRef(
99
+ const UnstyledLink = forwardRef(
100
100
  ({ onClick, noTrack, externalLinkIcon, ...props }, ref) => {
101
101
  const classes = useStyles();
102
102
  const analytics = useAnalytics();
@@ -118,7 +118,7 @@ const Link = forwardRef(
118
118
  return external ? (
119
119
  // External links
120
120
  /* @__PURE__ */ jsxs(
121
- MaterialLink,
121
+ "a",
122
122
  {
123
123
  ...newWindow ? { target: "_blank", rel: "noopener" } : {},
124
124
  ...props,
@@ -136,19 +136,13 @@ const Link = forwardRef(
136
136
  )
137
137
  ) : (
138
138
  // Interact with React Router for internal links
139
- /* @__PURE__ */ jsx(
140
- MaterialLink,
141
- {
142
- ...props,
143
- ref,
144
- component: Link$1,
145
- to,
146
- onClick: handleClick
147
- }
148
- )
139
+ /* @__PURE__ */ jsx(Link$1, { ...props, ref, to, onClick: handleClick })
149
140
  );
150
141
  }
151
142
  );
143
+ const Link = forwardRef((props, ref) => {
144
+ return /* @__PURE__ */ jsx(MaterialLink, { ...props, ref, component: UnstyledLink });
145
+ });
152
146
 
153
- export { Link, isExternalUri, isReactRouterBeta, useResolvedPath };
147
+ export { Link, UnstyledLink, isExternalUri, isReactRouterBeta, useResolvedPath };
154
148
  //# sourceMappingURL=Link.esm.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Link.esm.js","sources":["../../../src/components/Link/Link.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 */\nimport {\n configApiRef,\n useAnalytics,\n useApi,\n useApp,\n} from '@backstage/core-plugin-api';\n// eslint-disable-next-line no-restricted-imports\nimport MaterialLink, {\n LinkProps as MaterialLinkProps,\n} from '@material-ui/core/Link';\nimport { makeStyles } from '@material-ui/core/styles';\nimport Typography from '@material-ui/core/Typography';\nimport classnames from 'classnames';\nimport { trimEnd } from 'lodash';\nimport {\n ReactNode,\n ReactElement,\n MouseEvent as ReactMouseEvent,\n ElementType,\n forwardRef,\n} from 'react';\nimport {\n createRoutesFromChildren,\n Link as RouterLink,\n LinkProps as RouterLinkProps,\n Route,\n} from 'react-router-dom';\nimport OpenInNew from '@material-ui/icons/OpenInNew';\n\nexport function isReactRouterBeta(): boolean {\n const [obj] = createRoutesFromChildren(<Route index element={<div />} />);\n return !obj.index;\n}\n\n/** @public */\nexport type LinkClassKey = 'visuallyHidden' | 'externalLink';\n\nconst useStyles = makeStyles(\n theme => ({\n visuallyHidden: {\n clip: 'rect(0 0 0 0)',\n clipPath: 'inset(50%)',\n overflow: 'hidden',\n position: 'absolute',\n userSelect: 'none',\n whiteSpace: 'nowrap',\n height: 1,\n width: 1,\n },\n externalLink: {\n position: 'relative',\n },\n externalLinkIcon: {\n verticalAlign: 'bottom',\n marginLeft: theme.spacing(0.5),\n },\n }),\n { name: 'Link' },\n);\n\nconst ExternalLinkIcon = () => {\n const app = useApp();\n const Icon = app.getSystemIcon('externalLink') || OpenInNew;\n const classes = useStyles();\n return <Icon className={classes.externalLinkIcon} />;\n};\n\nexport const isExternalUri = (uri: string) => /^([a-z+.-]+):/.test(uri);\n\n// See https://github.com/facebook/react/blob/f0cf832e1d0c8544c36aa8b310960885a11a847c/packages/react-dom-bindings/src/shared/sanitizeURL.js\nconst scriptProtocolPattern =\n // eslint-disable-next-line no-control-regex\n /^[\\u0000-\\u001F ]*j[\\r\\n\\t]*a[\\r\\n\\t]*v[\\r\\n\\t]*a[\\r\\n\\t]*s[\\r\\n\\t]*c[\\r\\n\\t]*r[\\r\\n\\t]*i[\\r\\n\\t]*p[\\r\\n\\t]*t[\\r\\n\\t]*\\:/i;\n\n// We install this globally in order to prevent javascript: URL XSS attacks via window.open\nconst originalWindowOpen = window.open as typeof window.open & {\n __backstage?: true;\n};\nif (originalWindowOpen && !originalWindowOpen.__backstage) {\n const newOpen = function open(\n this: Window,\n ...args: Parameters<typeof window.open>\n ) {\n const url = String(args[0]);\n if (scriptProtocolPattern.test(url)) {\n throw new Error(\n 'Rejected window.open() with a javascript: URL as a security precaution',\n );\n }\n return originalWindowOpen.apply(this, args);\n };\n newOpen.__backstage = true;\n window.open = newOpen;\n}\n\nexport type LinkProps = Omit<MaterialLinkProps, 'to'> &\n Omit<RouterLinkProps, 'to'> & {\n to: string;\n component?: ElementType<any>;\n noTrack?: boolean;\n externalLinkIcon?: boolean;\n };\n\n/**\n * Returns the app base url that could be empty if the Config API is not properly implemented.\n * The only cases there would be no Config API are in tests and in storybook stories, and in those cases, it's unlikely that callers would rely on this subpath behavior.\n */\nconst useBaseUrl = () => {\n try {\n const config = useApi(configApiRef);\n return config.getOptionalString('app.baseUrl');\n } catch {\n return undefined;\n }\n};\n\n/**\n * Get the app base path from the configured app baseUrl.\n * The returned path does not have a trailing slash.\n */\nconst useBasePath = () => {\n // baseUrl can be specified as just a path\n const base = 'http://sample.dev';\n const url = useBaseUrl() ?? '/';\n const { pathname } = new URL(url, base);\n return trimEnd(pathname, '/');\n};\n\n/** @deprecated Remove once we no longer support React Router v6 beta */\nexport const useResolvedPath = (uri: LinkProps['to']) => {\n let resolvedPath = String(uri);\n\n const basePath = useBasePath();\n const external = isExternalUri(resolvedPath);\n const startsWithBasePath = resolvedPath.startsWith(basePath);\n\n if (!external && !startsWithBasePath) {\n resolvedPath = basePath.concat(resolvedPath);\n }\n\n return resolvedPath;\n};\n\n/**\n * Given a react node, try to retrieve its text content.\n */\nconst getNodeText = (node: ReactNode): string => {\n // If the node is an array of children, recurse and join.\n if (node instanceof Array) {\n return node.map(getNodeText).join(' ').trim();\n }\n\n // If the node is a react element, recurse on its children.\n if (typeof node === 'object' && node) {\n return getNodeText((node as ReactElement)?.props?.children);\n }\n\n // Base case: the node is just text. Return it.\n if (['string', 'number'].includes(typeof node)) {\n return String(node);\n }\n\n // Base case: just return an empty string.\n return '';\n};\n\n/**\n * Thin wrapper on top of material-ui's Link component, which...\n * - Makes the Link use react-router\n * - Captures Link clicks as analytics events.\n */\nexport const Link = forwardRef<any, LinkProps>(\n ({ onClick, noTrack, externalLinkIcon, ...props }, ref) => {\n const classes = useStyles();\n const analytics = useAnalytics();\n\n // Adding the base path to URLs breaks react-router v6 stable, so we only\n // do it for beta. The react router version won't change at runtime so it is\n // fine to ignore the rules of hooks.\n // eslint-disable-next-line react-hooks/rules-of-hooks\n const to = isReactRouterBeta() ? useResolvedPath(props.to) : props.to;\n const linkText = getNodeText(props.children) || to;\n const external = isExternalUri(to);\n const newWindow = external && !!/^https?:/.exec(to);\n\n if (scriptProtocolPattern.test(to)) {\n throw new Error(\n 'Link component rejected javascript: URL as a security precaution',\n );\n }\n\n const handleClick = (event: ReactMouseEvent<any, MouseEvent>) => {\n onClick?.(event);\n if (!noTrack) {\n analytics.captureEvent('click', linkText, { attributes: { to } });\n }\n };\n\n return external ? (\n // External links\n <MaterialLink\n {...(newWindow ? { target: '_blank', rel: 'noopener' } : {})}\n {...props}\n {...(props['aria-label']\n ? { 'aria-label': `${props['aria-label']}, Opens in a new window` }\n : {})}\n ref={ref}\n href={to}\n onClick={handleClick}\n className={classnames(classes.externalLink, props.className)}\n >\n {props.children}\n {externalLinkIcon && <ExternalLinkIcon />}\n <Typography component=\"span\" className={classes.visuallyHidden}>\n , Opens in a new window\n </Typography>\n </MaterialLink>\n ) : (\n // Interact with React Router for internal links\n <MaterialLink\n {...props}\n ref={ref}\n component={RouterLink}\n to={to}\n onClick={handleClick}\n />\n );\n },\n) as (props: LinkProps) => JSX.Element;\n"],"names":["classnames","RouterLink"],"mappings":"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"}
1
+ {"version":3,"file":"Link.esm.js","sources":["../../../src/components/Link/Link.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 */\nimport {\n configApiRef,\n useAnalytics,\n useApi,\n useApp,\n} from '@backstage/core-plugin-api';\n// eslint-disable-next-line no-restricted-imports\nimport MaterialLink, {\n LinkProps as MaterialLinkProps,\n} from '@material-ui/core/Link';\nimport { makeStyles } from '@material-ui/core/styles';\nimport Typography from '@material-ui/core/Typography';\nimport classnames from 'classnames';\nimport { trimEnd } from 'lodash';\nimport {\n ReactNode,\n ReactElement,\n MouseEvent as ReactMouseEvent,\n ElementType,\n forwardRef,\n} from 'react';\nimport {\n createRoutesFromChildren,\n Link as RouterLink,\n LinkProps as RouterLinkProps,\n Route,\n} from 'react-router-dom';\nimport OpenInNew from '@material-ui/icons/OpenInNew';\n\nexport function isReactRouterBeta(): boolean {\n const [obj] = createRoutesFromChildren(<Route index element={<div />} />);\n return !obj.index;\n}\n\n/** @public */\nexport type LinkClassKey = 'visuallyHidden' | 'externalLink';\n\nconst useStyles = makeStyles(\n theme => ({\n visuallyHidden: {\n clip: 'rect(0 0 0 0)',\n clipPath: 'inset(50%)',\n overflow: 'hidden',\n position: 'absolute',\n userSelect: 'none',\n whiteSpace: 'nowrap',\n height: 1,\n width: 1,\n },\n externalLink: {\n position: 'relative',\n },\n externalLinkIcon: {\n verticalAlign: 'bottom',\n marginLeft: theme.spacing(0.5),\n },\n }),\n { name: 'Link' },\n);\n\nconst ExternalLinkIcon = () => {\n const app = useApp();\n const Icon = app.getSystemIcon('externalLink') || OpenInNew;\n const classes = useStyles();\n return <Icon className={classes.externalLinkIcon} />;\n};\n\nexport const isExternalUri = (uri: string) => /^([a-z+.-]+):/.test(uri);\n\n// See https://github.com/facebook/react/blob/f0cf832e1d0c8544c36aa8b310960885a11a847c/packages/react-dom-bindings/src/shared/sanitizeURL.js\nconst scriptProtocolPattern =\n // eslint-disable-next-line no-control-regex\n /^[\\u0000-\\u001F ]*j[\\r\\n\\t]*a[\\r\\n\\t]*v[\\r\\n\\t]*a[\\r\\n\\t]*s[\\r\\n\\t]*c[\\r\\n\\t]*r[\\r\\n\\t]*i[\\r\\n\\t]*p[\\r\\n\\t]*t[\\r\\n\\t]*\\:/i;\n\n// We install this globally in order to prevent javascript: URL XSS attacks via window.open\nconst originalWindowOpen = window.open as typeof window.open & {\n __backstage?: true;\n};\nif (originalWindowOpen && !originalWindowOpen.__backstage) {\n const newOpen = function open(\n this: Window,\n ...args: Parameters<typeof window.open>\n ) {\n const url = String(args[0]);\n if (scriptProtocolPattern.test(url)) {\n throw new Error(\n 'Rejected window.open() with a javascript: URL as a security precaution',\n );\n }\n return originalWindowOpen.apply(this, args);\n };\n newOpen.__backstage = true;\n window.open = newOpen;\n}\n\nexport type LinkProps = Omit<MaterialLinkProps, 'to'> &\n Omit<RouterLinkProps, 'to'> & {\n to: string;\n component?: ElementType<any>;\n noTrack?: boolean;\n externalLinkIcon?: boolean;\n };\n\n/**\n * Returns the app base url that could be empty if the Config API is not properly implemented.\n * The only cases there would be no Config API are in tests and in storybook stories, and in those cases, it's unlikely that callers would rely on this subpath behavior.\n */\nconst useBaseUrl = () => {\n try {\n const config = useApi(configApiRef);\n return config.getOptionalString('app.baseUrl');\n } catch {\n return undefined;\n }\n};\n\n/**\n * Get the app base path from the configured app baseUrl.\n * The returned path does not have a trailing slash.\n */\nconst useBasePath = () => {\n // baseUrl can be specified as just a path\n const base = 'http://sample.dev';\n const url = useBaseUrl() ?? '/';\n const { pathname } = new URL(url, base);\n return trimEnd(pathname, '/');\n};\n\n/** @deprecated Remove once we no longer support React Router v6 beta */\nexport const useResolvedPath = (uri: LinkProps['to']) => {\n let resolvedPath = String(uri);\n\n const basePath = useBasePath();\n const external = isExternalUri(resolvedPath);\n const startsWithBasePath = resolvedPath.startsWith(basePath);\n\n if (!external && !startsWithBasePath) {\n resolvedPath = basePath.concat(resolvedPath);\n }\n\n return resolvedPath;\n};\n\n/**\n * Given a react node, try to retrieve its text content.\n */\nconst getNodeText = (node: ReactNode): string => {\n // If the node is an array of children, recurse and join.\n if (node instanceof Array) {\n return node.map(getNodeText).join(' ').trim();\n }\n\n // If the node is a react element, recurse on its children.\n if (typeof node === 'object' && node) {\n return getNodeText((node as ReactElement)?.props?.children);\n }\n\n // Base case: the node is just text. Return it.\n if (['string', 'number'].includes(typeof node)) {\n return String(node);\n }\n\n // Base case: just return an empty string.\n return '';\n};\n\n/**\n * Unstyled link primitive which...\n * - Uses react-router for internal links.\n * - Captures link clicks as analytics events.\n */\nexport const UnstyledLink = forwardRef<any, LinkProps>(\n ({ onClick, noTrack, externalLinkIcon, ...props }, ref) => {\n const classes = useStyles();\n const analytics = useAnalytics();\n\n // Adding the base path to URLs breaks react-router v6 stable, so we only\n // do it for beta. The react router version won't change at runtime so it is\n // fine to ignore the rules of hooks.\n // eslint-disable-next-line react-hooks/rules-of-hooks\n const to = isReactRouterBeta() ? useResolvedPath(props.to) : props.to;\n const linkText = getNodeText(props.children) || to;\n const external = isExternalUri(to);\n const newWindow = external && !!/^https?:/.exec(to);\n\n if (scriptProtocolPattern.test(to)) {\n throw new Error(\n 'Link component rejected javascript: URL as a security precaution',\n );\n }\n\n const handleClick = (event: ReactMouseEvent<any, MouseEvent>) => {\n onClick?.(event);\n if (!noTrack) {\n analytics.captureEvent('click', linkText, { attributes: { to } });\n }\n };\n\n return external ? (\n // External links\n <a\n {...(newWindow ? { target: '_blank', rel: 'noopener' } : {})}\n {...props}\n {...(props['aria-label']\n ? { 'aria-label': `${props['aria-label']}, Opens in a new window` }\n : {})}\n ref={ref}\n href={to}\n onClick={handleClick}\n className={classnames(classes.externalLink, props.className)}\n >\n {props.children}\n {externalLinkIcon && <ExternalLinkIcon />}\n <Typography component=\"span\" className={classes.visuallyHidden}>\n , Opens in a new window\n </Typography>\n </a>\n ) : (\n // Interact with React Router for internal links\n <RouterLink {...props} ref={ref} to={to} onClick={handleClick} />\n );\n },\n);\n\n/**\n * Thin wrapper combining UnstyledLink with material-ui's Link component.\n */\nexport const Link = forwardRef<any, LinkProps>((props, ref) => {\n return <MaterialLink {...props} ref={ref} component={UnstyledLink} />;\n}) as (props: LinkProps) => JSX.Element;\n"],"names":["classnames","RouterLink"],"mappings":"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"}
@@ -1,9 +1,9 @@
1
1
  import { jsx } from 'react/jsx-runtime';
2
2
  import Button$1 from '@material-ui/core/Button';
3
3
  import { forwardRef } from 'react';
4
- import { Link } from '../Link/Link.esm.js';
4
+ import { UnstyledLink } from '../Link/Link.esm.js';
5
5
 
6
- const LinkWrapper = forwardRef((props, ref) => /* @__PURE__ */ jsx(Link, { ref, ...props, color: "initial" }));
6
+ const LinkWrapper = forwardRef((props, ref) => /* @__PURE__ */ jsx(UnstyledLink, { ref, ...props, color: "initial" }));
7
7
  const LinkButton = forwardRef((props, ref) => /* @__PURE__ */ jsx(Button$1, { ref, component: LinkWrapper, ...props }));
8
8
  const Button = LinkButton;
9
9
 
@@ -1 +1 @@
1
- {"version":3,"file":"LinkButton.esm.js","sources":["../../../src/components/LinkButton/LinkButton.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 MaterialButton, {\n ButtonProps as MaterialButtonProps,\n} from '@material-ui/core/Button';\nimport { forwardRef } from 'react';\nimport { Link, LinkProps } from '../Link';\n\n/**\n * Properties for {@link LinkButton}\n *\n * @public\n * @remarks\n *\n * See {@link https://v4.mui.com/api/button/#props | Material UI Button Props} for all properties\n */\nexport type LinkButtonProps = MaterialButtonProps &\n Omit<LinkProps, 'variant' | 'color'>;\n\n/**\n * This wrapper is here to reset the color of the Link and make typescript happy.\n */\nconst LinkWrapper = forwardRef<any, LinkProps>((props, ref) => (\n <Link ref={ref} {...props} color=\"initial\" />\n));\n\n/**\n * Thin wrapper on top of material-ui's {@link https://v4.mui.com/components/buttons/ | Button} component\n *\n * @public\n * @remarks\n */\nexport const LinkButton = forwardRef<any, LinkButtonProps>((props, ref) => (\n <MaterialButton ref={ref} component={LinkWrapper} {...props} />\n)) as (props: LinkButtonProps) => JSX.Element;\n\n/**\n * @public\n * @deprecated use LinkButton instead\n */\nexport const Button = LinkButton;\n\n/**\n * @public\n * @deprecated use LinkButtonProps instead\n */\nexport type ButtonProps = LinkButtonProps;\n"],"names":["MaterialButton"],"mappings":";;;;;AAoCA,MAAM,WAAA,GAAc,UAAA,CAA2B,CAAC,KAAA,EAAO,GAAA,qBACrD,GAAA,CAAC,IAAA,EAAA,EAAK,GAAA,EAAW,GAAG,KAAA,EAAO,KAAA,EAAM,SAAA,EAAU,CAC5C,CAAA;AAQM,MAAM,UAAA,GAAa,UAAA,CAAiC,CAAC,KAAA,EAAO,GAAA,qBACjE,GAAA,CAACA,QAAA,EAAA,EAAe,GAAA,EAAU,SAAA,EAAW,WAAA,EAAc,GAAG,KAAA,EAAO,CAC9D;AAMM,MAAM,MAAA,GAAS;;;;"}
1
+ {"version":3,"file":"LinkButton.esm.js","sources":["../../../src/components/LinkButton/LinkButton.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 MaterialButton, {\n ButtonProps as MaterialButtonProps,\n} from '@material-ui/core/Button';\nimport { forwardRef } from 'react';\nimport { UnstyledLink, LinkProps } from '../Link/Link';\n\n/**\n * Properties for {@link LinkButton}\n *\n * @public\n * @remarks\n *\n * See {@link https://v4.mui.com/api/button/#props | Material UI Button Props} for all properties\n */\nexport type LinkButtonProps = MaterialButtonProps &\n Omit<LinkProps, 'variant' | 'color'>;\n\n/**\n * This wrapper is here to reset the color of the Link and make typescript happy.\n */\nconst LinkWrapper = forwardRef<any, LinkProps>((props, ref) => (\n <UnstyledLink ref={ref} {...props} color=\"initial\" />\n));\n\n/**\n * Thin wrapper on top of material-ui's {@link https://v4.mui.com/components/buttons/ | Button} component\n *\n * @public\n * @remarks\n */\nexport const LinkButton = forwardRef<any, LinkButtonProps>((props, ref) => (\n <MaterialButton ref={ref} component={LinkWrapper} {...props} />\n)) as (props: LinkButtonProps) => JSX.Element;\n\n/**\n * @public\n * @deprecated use LinkButton instead\n */\nexport const Button = LinkButton;\n\n/**\n * @public\n * @deprecated use LinkButtonProps instead\n */\nexport type ButtonProps = LinkButtonProps;\n"],"names":["MaterialButton"],"mappings":";;;;;AAoCA,MAAM,WAAA,GAAc,UAAA,CAA2B,CAAC,KAAA,EAAO,GAAA,qBACrD,GAAA,CAAC,YAAA,EAAA,EAAa,GAAA,EAAW,GAAG,KAAA,EAAO,KAAA,EAAM,SAAA,EAAU,CACpD,CAAA;AAQM,MAAM,UAAA,GAAa,UAAA,CAAiC,CAAC,KAAA,EAAO,GAAA,qBACjE,GAAA,CAACA,QAAA,EAAA,EAAe,GAAA,EAAU,SAAA,EAAW,WAAA,EAAc,GAAG,KAAA,EAAO,CAC9D;AAMM,MAAM,MAAA,GAAS;;;;"}
package/dist/index.d.ts CHANGED
@@ -153,9 +153,7 @@ type LinkProps = Omit<LinkProps$1, 'to'> & Omit<LinkProps$2, 'to'> & {
153
153
  externalLinkIcon?: boolean;
154
154
  };
155
155
  /**
156
- * Thin wrapper on top of material-ui's Link component, which...
157
- * - Makes the Link use react-router
158
- * - Captures Link clicks as analytics events.
156
+ * Thin wrapper combining UnstyledLink with material-ui's Link component.
159
157
  */
160
158
  declare const Link: (props: LinkProps) => JSX.Element;
161
159
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backstage/core-components",
3
- "version": "0.18.6",
3
+ "version": "0.18.7-next.1",
4
4
  "description": "Core components used by Backstage plugins and apps",
5
5
  "backstage": {
6
6
  "role": "web-library"
@@ -66,11 +66,11 @@
66
66
  "test": "backstage-cli package test"
67
67
  },
68
68
  "dependencies": {
69
- "@backstage/config": "^1.3.6",
70
- "@backstage/core-plugin-api": "^1.12.2",
71
- "@backstage/errors": "^1.2.7",
72
- "@backstage/theme": "^0.7.1",
73
- "@backstage/version-bridge": "^1.0.11",
69
+ "@backstage/config": "1.3.6",
70
+ "@backstage/core-plugin-api": "1.12.3-next.0",
71
+ "@backstage/errors": "1.2.7",
72
+ "@backstage/theme": "0.7.2-next.0",
73
+ "@backstage/version-bridge": "1.0.11",
74
74
  "@dagrejs/dagre": "^1.1.4",
75
75
  "@date-io/core": "^1.3.13",
76
76
  "@material-table/core": "^3.1.0",
@@ -110,10 +110,10 @@
110
110
  "zod": "^3.25.76"
111
111
  },
112
112
  "devDependencies": {
113
- "@backstage/app-defaults": "^1.7.4",
114
- "@backstage/cli": "^0.35.3",
115
- "@backstage/core-app-api": "^1.19.4",
116
- "@backstage/test-utils": "^1.7.14",
113
+ "@backstage/app-defaults": "1.7.5-next.1",
114
+ "@backstage/cli": "0.35.4-next.1",
115
+ "@backstage/core-app-api": "1.19.5-next.0",
116
+ "@backstage/test-utils": "1.7.15-next.1",
117
117
  "@testing-library/dom": "^10.0.0",
118
118
  "@testing-library/jest-dom": "^6.0.0",
119
119
  "@testing-library/user-event": "^14.0.0",