Add NavLink, NavButton components

This commit is contained in:
Ivan Shmidt
2020-06-03 16:39:07 +02:00
parent 3276db8716
commit 239c1a0e3f
9 changed files with 363 additions and 0 deletions
@@ -0,0 +1,95 @@
/*
* Copyright 2020 Spotify AB
*
* 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, { FunctionComponentFactory } from 'react';
import { NavButton } from './NavButton';
import {
MemoryRouter,
Route,
useLocation,
NavLink as RouterNavLink,
} from 'react-router-dom';
import { createRouteRef } from '@backstage/core-api';
const Location = () => {
const location = useLocation();
return <pre>Current location: {location.pathname}</pre>;
};
export default {
title: 'NavButton',
component: NavButton,
decorators: [
(storyFn: FunctionComponentFactory<{}>) => (
<MemoryRouter>
<div>
<div>
<Location />
</div>
{storyFn()}
</div>
</MemoryRouter>
),
],
};
export const Default = () => {
const routeRef = createRouteRef({
icon: () => null,
path: '/hello',
title: 'Hi there!',
});
return (
<>
<NavButton to={routeRef.path}>This button</NavButton>&nbsp;will utilise
the react-router MemoryRouter's navigation
<Route path={routeRef.path}>
<h1>{routeRef.title}</h1>
</Route>
</>
);
};
export const PassProps = () => {
const routeRef = createRouteRef({
icon: () => null,
path: '/hello',
title: 'Hi there!',
});
return (
<>
<NavButton
to={routeRef.path}
/** react-router-dom related prop */
component={RouterNavLink}
/** material-ui related prop */
color="secondary"
variant="outlined"
>
This link
</NavButton>
&nbsp;has props for both material-ui's component as well as for
react-router-dom's
<Route path={routeRef.path}>
<h1>{routeRef.title}</h1>
</Route>
</>
);
};
PassProps.story = {
name: `Accepts material-ui Button's and react-router-dom Link's props`,
};
@@ -0,0 +1,40 @@
/*
* Copyright 2020 Spotify AB
*
* 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 } from '@testing-library/react';
import { wrapInTestApp } from '@backstage/test-utils';
import { NavButton } from './NavButton';
import { MemoryRouter, Route } from 'react-router';
import { act } from 'react-dom/test-utils';
describe('<NavButton />', () => {
it('navigates using react-router', async () => {
const testString = 'This is test string';
const buttonLabel = 'Navigate!';
const { getByText } = render(
wrapInTestApp(
<MemoryRouter>
<NavButton to="/test">{buttonLabel}</NavButton>
<Route path="/test">{testString}</Route>{' '}
</MemoryRouter>,
),
);
expect(() => getByText(testString)).toThrow();
await act(async () => fireEvent.click(getByText(buttonLabel)));
expect(getByText(testString)).toBeInTheDocument();
});
});
@@ -0,0 +1,29 @@
/*
* Copyright 2020 Spotify AB
*
* 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, { ComponentProps } from 'react';
import { Button } from '@material-ui/core';
import { Link as RouterLink } from 'react-router-dom';
type Props = ComponentProps<typeof Button> & ComponentProps<typeof RouterLink>;
/**
* Thin wrapper on top of material-ui's Button component
* Makes the Button to utilise react-router
*/
export const NavButton = React.forwardRef<any, Props>((props, ref) => (
<Button ref={ref} component={RouterLink} {...props} />
));
@@ -0,0 +1,17 @@
/*
* Copyright 2020 Spotify AB
*
* 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 { NavButton } from './NavButton';
@@ -0,0 +1,94 @@
/*
* Copyright 2020 Spotify AB
*
* 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, { FunctionComponentFactory } from 'react';
import { NavLink } from './NavLink';
import {
MemoryRouter,
Route,
useLocation,
NavLink as RouterNavLink,
} from 'react-router-dom';
import { createRouteRef } from '@backstage/core-api';
const Location = () => {
const location = useLocation();
return <pre>Current location: {location.pathname}</pre>;
};
export default {
title: 'NavLink',
component: NavLink,
decorators: [
(storyFn: FunctionComponentFactory<{}>) => (
<MemoryRouter>
<div>
<div>
<Location />
</div>
{storyFn()}
</div>
</MemoryRouter>
),
],
};
export const Default = () => {
const routeRef = createRouteRef({
icon: () => null,
path: '/hello',
title: 'Hi there!',
});
return (
<>
<NavLink to={routeRef.path}>This link</NavLink>&nbsp;will utilise the
react-router MemoryRouter's navigation
<Route path={routeRef.path}>
<h1>{routeRef.title}</h1>
</Route>
</>
);
};
export const PassProps = () => {
const routeRef = createRouteRef({
icon: () => null,
path: '/hello',
title: 'Hi there!',
});
return (
<>
<NavLink
to={routeRef.path}
/** react-router-dom related prop */
component={RouterNavLink}
/** material-ui related prop */
color="secondary"
>
This link
</NavLink>
&nbsp;has props for both material-ui's component as well as for
react-router-dom's
<Route path={routeRef.path}>
<h1>{routeRef.title}</h1>
</Route>
</>
);
};
PassProps.story = {
name: `Accepts material-ui Link's and react-router-dom Link's props`,
};
@@ -0,0 +1,40 @@
/*
* Copyright 2020 Spotify AB
*
* 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 } from '@testing-library/react';
import { wrapInTestApp } from '@backstage/test-utils';
import { NavLink } from './NavLink';
import { MemoryRouter, Route } from 'react-router';
import { act } from 'react-dom/test-utils';
describe('<NavLink />', () => {
it('navigates using react-router', async () => {
const testString = 'This is test string';
const linkText = 'Navigate!';
const { getByText } = render(
wrapInTestApp(
<MemoryRouter>
<NavLink to="/test">{linkText}</NavLink>
<Route path="/test">{testString}</Route>{' '}
</MemoryRouter>,
),
);
expect(() => getByText(testString)).toThrow();
await act(async () => fireEvent.click(getByText(linkText)));
expect(getByText(testString)).toBeInTheDocument();
});
});
@@ -0,0 +1,29 @@
/*
* Copyright 2020 Spotify AB
*
* 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, { ComponentProps } from 'react';
import { Link } from '@material-ui/core';
import { Link as RouterLink } from 'react-router-dom';
type Props = ComponentProps<typeof Link> & ComponentProps<typeof RouterLink>;
/**
* Thin wrapper on top of material-ui's Link component
* Makes the Link to utilise react-router
*/
export const NavLink = React.forwardRef<any, Props>((props, ref) => (
<Link ref={ref} component={RouterLink} {...props} />
));
@@ -0,0 +1,17 @@
/*
* Copyright 2020 Spotify AB
*
* 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 { NavLink } from './NavLink';
+2
View File
@@ -38,4 +38,6 @@ export { default as StructuredMetadataTable } from './components/StructuredMetad
export { default as TrendLine } from './components/TrendLine';
export { FeatureCalloutCircular } from './components/FeatureDiscovery/FeatureCalloutCircular';
export * from './components/Status';
export * from './components/NavButton';
export * from './components/NavLink';
export { default as WarningPanel } from './components/WarningPanel';