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';