From bf91c9f896dd8de3eae3e23a6326a32d48be4560 Mon Sep 17 00:00:00 2001
From: Kurt King
Date: Tue, 20 Dec 2022 20:06:35 -0600
Subject: [PATCH] refactor: add LinkButton in favor of Button
Signed-off-by: Kurt King
---
.../src/components/Button/Button.tsx | 4 +-
.../LinkButton/LinkButton.stories.tsx | 170 ++++++++++++++++++
.../components/LinkButton/LinkButton.test.tsx | 44 +++++
.../src/components/LinkButton/LinkButton.tsx | 53 ++++++
.../src/components/LinkButton/index.ts | 17 ++
.../core-components/src/components/index.ts | 1 +
6 files changed, 287 insertions(+), 2 deletions(-)
create mode 100644 packages/core-components/src/components/LinkButton/LinkButton.stories.tsx
create mode 100644 packages/core-components/src/components/LinkButton/LinkButton.test.tsx
create mode 100644 packages/core-components/src/components/LinkButton/LinkButton.tsx
create mode 100644 packages/core-components/src/components/LinkButton/index.ts
diff --git a/packages/core-components/src/components/Button/Button.tsx b/packages/core-components/src/components/Button/Button.tsx
index 575f42b03c..771d94adb5 100644
--- a/packages/core-components/src/components/Button/Button.tsx
+++ b/packages/core-components/src/components/Button/Button.tsx
@@ -25,6 +25,7 @@ import { Link, LinkProps } from '../Link';
*
* @public
* @remarks
+ * @deprecated use `LinkButtonProps` instead
*
* See {@link https://v4.mui.com/api/button/#props | Material-UI Button Props} for all properties
*/
@@ -43,8 +44,7 @@ const LinkWrapper = React.forwardRef((props, ref) => (
*
* @public
* @remarks
- *
- * Makes the Button to utilize react-router
+ * @deprecated use `LinkButton` instead
*/
export const Button = React.forwardRef((props, ref) => (
diff --git a/packages/core-components/src/components/LinkButton/LinkButton.stories.tsx b/packages/core-components/src/components/LinkButton/LinkButton.stories.tsx
new file mode 100644
index 0000000000..3dc49abb7f
--- /dev/null
+++ b/packages/core-components/src/components/LinkButton/LinkButton.stories.tsx
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2020 The Backstage Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import React, { ComponentType } from 'react';
+import { LinkButton } from './LinkButton';
+import { useLocation } from 'react-router-dom';
+import { createRouteRef, useRouteRef } from '@backstage/core-plugin-api';
+import Divider from '@material-ui/core/Divider';
+import List from '@material-ui/core/List';
+import ListItem from '@material-ui/core/ListItem';
+import ListItemText from '@material-ui/core/ListItemText';
+import Typography from '@material-ui/core/Typography';
+import MaterialButton from '@material-ui/core/Button';
+import { wrapInTestApp } from '@backstage/test-utils';
+import { Link } from '../Link';
+
+const routeRef = createRouteRef({
+ id: 'storybook.test-route',
+});
+
+const Location = () => {
+ const location = useLocation();
+ return
Current location: {location.pathname}
;
+};
+
+export default {
+ title: 'Inputs/Button',
+ component: LinkButton,
+ decorators: [
+ (Story: ComponentType<{}>) =>
+ wrapInTestApp(
+ <>
+
+ A collection of buttons that should be used in the Backstage
+ interface. These leverage the properties inherited from{' '}
+
+ Material-UI Button
+
+ , but include an opinionated set that align to the Backstage design.
+
+
+
+
+
+
+
+
+
+
+ >,
+ { mountedRoutes: { '/hello': routeRef } },
+ ),
+ ],
+};
+
+export const Default = () => {
+ const link = useRouteRef(routeRef);
+ // Design Permutations:
+ // color = default | primary | secondary
+ // variant = contained | outlined | text
+ return (
+
+
+
+ Default Button:
+ This is the default button design which should be used in most cases.
+
+
color="primary" variant="contained"
+
+
+
+ Register Component
+
+
+
+
+ Secondary Button:
+ Used for actions that cancel, skip, and in general perform negative
+ functions, etc.
+
+
color="secondary" variant="contained"
+
+
+
+ Cancel
+
+
+
+
+ Tertiary Button:
+ Used commonly in a ButtonGroup and when the button function itself is
+ not a primary function on a page.
+
+
color="default" variant="outlined"
+
+
+
+ View Details
+
+
+
+ );
+};
+
+export const ButtonLinks = () => {
+ const link = useRouteRef(routeRef);
+
+ const handleClick = () => {
+ return 'Your click worked!';
+ };
+
+ return (
+ <>
+
+ {
+ // TODO: Refactor to use new routing mechanisms
+ }
+
+
+ Route Ref
+
+ has props for both Material-UI's component as well as for
+ react-router-dom's Route object.
+
+
+
+
+ Static Path
+
+ links to a statically defined route. In general, this should be
+ avoided.
+
+
+
+
+ View URL
+
+ links to a defined URL using Material-UI's Button.
+
+
+
+
+ Trigger Event
+
+ triggers an onClick event using Material-UI's Button.
+
+
+ >
+ );
+};
diff --git a/packages/core-components/src/components/LinkButton/LinkButton.test.tsx b/packages/core-components/src/components/LinkButton/LinkButton.test.tsx
new file mode 100644
index 0000000000..7dbc42f9cb
--- /dev/null
+++ b/packages/core-components/src/components/LinkButton/LinkButton.test.tsx
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2020 The Backstage Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import React from 'react';
+import { render, fireEvent, act } from '@testing-library/react';
+import { wrapInTestApp } from '@backstage/test-utils';
+import { LinkButton } from './LinkButton';
+import { Route, Routes } from 'react-router-dom';
+
+describe('', () => {
+ it('navigates using react-router', async () => {
+ const testString = 'This is test string';
+ const linkButtonLabel = 'Navigate!';
+ const { getByText } = render(
+ wrapInTestApp(
+ <>
+ {linkButtonLabel}
+
+ {testString}
} />
+
+ >,
+ ),
+ );
+
+ expect(() => getByText(testString)).toThrow();
+ await act(async () => {
+ fireEvent.click(getByText(linkButtonLabel));
+ });
+ expect(getByText(testString)).toBeInTheDocument();
+ });
+});
diff --git a/packages/core-components/src/components/LinkButton/LinkButton.tsx b/packages/core-components/src/components/LinkButton/LinkButton.tsx
new file mode 100644
index 0000000000..60c218ba5f
--- /dev/null
+++ b/packages/core-components/src/components/LinkButton/LinkButton.tsx
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2020 The Backstage Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import MaterialButton, {
+ ButtonProps as MaterialButtonProps,
+} from '@material-ui/core/Button';
+import React from 'react';
+import { Link, LinkProps } from '../Link';
+
+/**
+ * Properties for {@link LinkButton}
+ *
+ * @public
+ * @remarks
+ *
+ * See {@link https://v4.mui.com/api/button/#props | Material-UI Button Props} for all properties
+ */
+export type LinkButtonProps = MaterialButtonProps &
+ Omit;
+
+/**
+ * This wrapper is here to reset the color of the Link and make typescript happy.
+ */
+const LinkWrapper = React.forwardRef((props, ref) => (
+
+));
+
+/**
+ * Thin wrapper on top of material-ui's {@link https://v4.mui.com/components/buttons/ | Button} component
+ *
+ * @public
+ * @remarks
+ *
+ * Makes the Button to utilize react-router
+ */
+export const LinkButton = React.forwardRef(
+ (props, ref) => (
+
+ ),
+) as (props: LinkButtonProps) => JSX.Element;
diff --git a/packages/core-components/src/components/LinkButton/index.ts b/packages/core-components/src/components/LinkButton/index.ts
new file mode 100644
index 0000000000..8ca699ecdd
--- /dev/null
+++ b/packages/core-components/src/components/LinkButton/index.ts
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2020 The Backstage Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+export { LinkButton } from './LinkButton';
+export type { LinkButtonProps } from './LinkButton';
diff --git a/packages/core-components/src/components/index.ts b/packages/core-components/src/components/index.ts
index 473fab8444..218df9043d 100644
--- a/packages/core-components/src/components/index.ts
+++ b/packages/core-components/src/components/index.ts
@@ -30,6 +30,7 @@ export * from './HeaderIconLinkRow';
export * from './HorizontalScrollGrid';
export * from './Lifecycle';
export * from './Link';
+export * from './LinkButton';
export * from './LogViewer';
export * from './MarkdownContent';
export * from './OAuthRequestDialog';