feat(search): add optional icon and secondary action for default result items
Co-authored-by: Emma Indal <emma.indahl@gmail.com> Signed-off-by: Camila Belo <camilaibs@gmail.com>
This commit is contained in:
+50
-5
@@ -16,6 +16,9 @@
|
||||
|
||||
import React from 'react';
|
||||
import { Grid } from '@material-ui/core';
|
||||
import FindInPageIcon from '@material-ui/icons/FindInPage';
|
||||
import GroupIcon from '@material-ui/icons/Group';
|
||||
import { Button } from '@backstage/core-components';
|
||||
import { DefaultResultListItem } from '../index';
|
||||
import { MemoryRouter } from 'react-router';
|
||||
|
||||
@@ -24,17 +27,59 @@ export default {
|
||||
component: DefaultResultListItem,
|
||||
};
|
||||
|
||||
const mockSearchResult = {
|
||||
location: 'search/search-result',
|
||||
title: 'Search Result 1',
|
||||
text: 'some text from the search result',
|
||||
owner: 'some-example-owner',
|
||||
};
|
||||
|
||||
export const Default = () => {
|
||||
return (
|
||||
<MemoryRouter>
|
||||
<Grid container direction="row">
|
||||
<Grid item xs={12}>
|
||||
<DefaultResultListItem result={mockSearchResult} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</MemoryRouter>
|
||||
);
|
||||
};
|
||||
|
||||
export const WithIcon = () => {
|
||||
return (
|
||||
<MemoryRouter>
|
||||
<Grid container direction="row">
|
||||
<Grid item xs={12}>
|
||||
<DefaultResultListItem
|
||||
result={{
|
||||
location: 'search/search-result',
|
||||
title: 'Search Result 1',
|
||||
text: 'some text from the search result',
|
||||
}}
|
||||
result={mockSearchResult}
|
||||
icon={<FindInPageIcon color="primary" />}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</MemoryRouter>
|
||||
);
|
||||
};
|
||||
|
||||
export const WithSecondaryAction = () => {
|
||||
return (
|
||||
<MemoryRouter>
|
||||
<Grid container direction="row">
|
||||
<Grid item xs={12}>
|
||||
<DefaultResultListItem
|
||||
result={mockSearchResult}
|
||||
secondaryAction={
|
||||
<Button
|
||||
to="#"
|
||||
size="small"
|
||||
aria-label="owner"
|
||||
variant="text"
|
||||
startIcon={<GroupIcon />}
|
||||
style={{ textTransform: 'lowercase' }}
|
||||
>
|
||||
{mockSearchResult.owner}
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
import React from 'react';
|
||||
import { screen } from '@testing-library/react';
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
|
||||
import FindInPageIcon from '@material-ui/icons/FindInPage';
|
||||
import { DefaultResultListItem } from './DefaultResultListItem';
|
||||
|
||||
describe('DefaultResultListItem', () => {
|
||||
@@ -25,6 +25,7 @@ describe('DefaultResultListItem', () => {
|
||||
title: 'title',
|
||||
text: 'text',
|
||||
location: '/location',
|
||||
owner: 'owner',
|
||||
};
|
||||
|
||||
it('Links to result.location', async () => {
|
||||
@@ -38,4 +39,21 @@ describe('DefaultResultListItem', () => {
|
||||
result.title + result.text,
|
||||
);
|
||||
});
|
||||
|
||||
it('should render icon if prop is specified', async () => {
|
||||
await renderInTestApp(
|
||||
<DefaultResultListItem
|
||||
result={result}
|
||||
icon={<FindInPageIcon title="icon" />}
|
||||
/>,
|
||||
);
|
||||
expect(screen.getByTitle('icon')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render secondary action if prop is specified', async () => {
|
||||
await renderInTestApp(
|
||||
<DefaultResultListItem result={result} secondaryAction={result.owner} />,
|
||||
);
|
||||
expect(screen.getByText('owner')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,24 +14,38 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React, { ReactNode } from 'react';
|
||||
import { IndexableDocument } from '@backstage/search-common';
|
||||
import { ListItem, ListItemText, Divider } from '@material-ui/core';
|
||||
import {
|
||||
ListItem,
|
||||
ListItemIcon,
|
||||
ListItemText,
|
||||
Box,
|
||||
Divider,
|
||||
} from '@material-ui/core';
|
||||
import { Link } from '@backstage/core-components';
|
||||
|
||||
type Props = {
|
||||
icon?: ReactNode;
|
||||
secondaryAction?: ReactNode;
|
||||
result: IndexableDocument;
|
||||
};
|
||||
|
||||
export const DefaultResultListItem = ({ result }: Props) => {
|
||||
export const DefaultResultListItem = ({
|
||||
result,
|
||||
icon,
|
||||
secondaryAction,
|
||||
}: Props) => {
|
||||
return (
|
||||
<Link to={result.location}>
|
||||
<ListItem alignItems="flex-start">
|
||||
<ListItem alignItems="center">
|
||||
{icon && <ListItemIcon>{icon}</ListItemIcon>}
|
||||
<ListItemText
|
||||
primaryTypographyProps={{ variant: 'h6' }}
|
||||
primary={result.title}
|
||||
secondary={result.text}
|
||||
/>
|
||||
{secondaryAction && <Box alignItems="flex-end">{secondaryAction}</Box>}
|
||||
</ListItem>
|
||||
<Divider />
|
||||
</Link>
|
||||
|
||||
Reference in New Issue
Block a user