mirror of https://github.com/grafana/grafana
Granfana ui/tag components (#22964)
* Add Tag component * Add Tag story * Add TagList * Group Tab and TabList * Fix typechecks * Remove Meta * Use forwardRef for the Tag * Add actions instead of console.log * Add previewspull/23027/head
parent
289a5fb862
commit
014e7d9261
@ -0,0 +1,23 @@ |
|||||||
|
import { Story, Preview, Props } from '@storybook/addon-docs/blocks'; |
||||||
|
import { Tag } from './Tag'; |
||||||
|
|
||||||
|
# Tag |
||||||
|
|
||||||
|
Used for displaying metadata, for example to add more details to search results. Background and border colors are generated from the tag name. |
||||||
|
|
||||||
|
<Preview> |
||||||
|
<div> |
||||||
|
<Tag name='Tag' onClick={(name) => console.log(name)} /> |
||||||
|
</div> |
||||||
|
</Preview> |
||||||
|
|
||||||
|
### Usage |
||||||
|
|
||||||
|
```jsx |
||||||
|
import { Tag } from '@grafana/ui'; |
||||||
|
|
||||||
|
<Tag name='Tag' onClick={(name) => console.log(name)} /> |
||||||
|
``` |
||||||
|
|
||||||
|
### Props |
||||||
|
<Props of={Tag} /> |
@ -0,0 +1,20 @@ |
|||||||
|
import React from 'react'; |
||||||
|
import { action } from '@storybook/addon-actions'; |
||||||
|
import { Tag } from './Tag'; |
||||||
|
import { withCenteredStory } from '../../utils/storybook/withCenteredStory'; |
||||||
|
import mdx from './Tag.mdx'; |
||||||
|
|
||||||
|
export default { |
||||||
|
title: 'General/Tags/Tag', |
||||||
|
component: Tag, |
||||||
|
decorators: [withCenteredStory], |
||||||
|
parameters: { |
||||||
|
docs: { |
||||||
|
page: mdx, |
||||||
|
}, |
||||||
|
}, |
||||||
|
}; |
||||||
|
|
||||||
|
export const single = () => { |
||||||
|
return <Tag name="Tag" onClick={action('Tag clicked')} />; |
||||||
|
}; |
@ -0,0 +1,52 @@ |
|||||||
|
import React, { forwardRef, HTMLAttributes } from 'react'; |
||||||
|
import { cx, css } from 'emotion'; |
||||||
|
import { GrafanaTheme } from '@grafana/data'; |
||||||
|
import { useTheme } from '../../themes'; |
||||||
|
import { getTagColorsFromName } from '../../utils'; |
||||||
|
|
||||||
|
export interface Props extends Omit<HTMLAttributes<HTMLElement>, 'onClick'> { |
||||||
|
/** Name of the tag to display */ |
||||||
|
name: string; |
||||||
|
onClick?: (name: string) => any; |
||||||
|
} |
||||||
|
|
||||||
|
export const Tag = forwardRef<HTMLElement, Props>(({ name, onClick, className, ...rest }, ref) => { |
||||||
|
const theme = useTheme(); |
||||||
|
const styles = getTagStyles(theme, name); |
||||||
|
|
||||||
|
const onTagClick = () => { |
||||||
|
if (onClick) { |
||||||
|
onClick(name); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
return ( |
||||||
|
<span key={name} ref={ref} onClick={onTagClick} className={cx(styles.wrapper, className)} {...rest}> |
||||||
|
{name} |
||||||
|
</span> |
||||||
|
); |
||||||
|
}); |
||||||
|
|
||||||
|
const getTagStyles = (theme: GrafanaTheme, name: string) => { |
||||||
|
const { borderColor, color } = getTagColorsFromName(name); |
||||||
|
return { |
||||||
|
wrapper: css` |
||||||
|
font-weight: ${theme.typography.weight.semibold}; |
||||||
|
font-size: ${theme.typography.size.sm}; |
||||||
|
line-height: ${theme.typography.lineHeight.xs}; |
||||||
|
vertical-align: baseline; |
||||||
|
background-color: ${color}; |
||||||
|
color: ${theme.colors.white}; |
||||||
|
white-space: nowrap; |
||||||
|
text-shadow: none; |
||||||
|
padding: 3px 6px; |
||||||
|
border: 1px solid ${borderColor}; |
||||||
|
border-radius: ${theme.border.radius.md}; |
||||||
|
|
||||||
|
:hover { |
||||||
|
opacity: 0.85; |
||||||
|
cursor: pointer; |
||||||
|
} |
||||||
|
`,
|
||||||
|
}; |
||||||
|
}; |
@ -0,0 +1,22 @@ |
|||||||
|
import { Story, Preview, Props } from '@storybook/addon-docs/blocks'; |
||||||
|
import { TagList } from './TagList'; |
||||||
|
|
||||||
|
# TagList |
||||||
|
|
||||||
|
List of tags with predefined margins and positioning. |
||||||
|
|
||||||
|
<Preview> |
||||||
|
<TagList tags={['datasource-test', 'gdev', 'mysql', 'mssql']} /> |
||||||
|
</Preview> |
||||||
|
|
||||||
|
### Usage |
||||||
|
|
||||||
|
```jsx |
||||||
|
import { TagList } from '@grafana/ui'; |
||||||
|
const tags = ['datasource-test', 'gdev', 'mysql', 'mssql']; |
||||||
|
|
||||||
|
<TagList tags={tags} onClick={tag => console.log(tag)} /> |
||||||
|
``` |
||||||
|
|
||||||
|
### Props |
||||||
|
<Props of={TagList} /> |
@ -0,0 +1,26 @@ |
|||||||
|
import React from 'react'; |
||||||
|
import { action } from '@storybook/addon-actions'; |
||||||
|
import { TagList } from './TagList'; |
||||||
|
import { withCenteredStory } from '../../utils/storybook/withCenteredStory'; |
||||||
|
import mdx from './TagList.mdx'; |
||||||
|
|
||||||
|
export default { |
||||||
|
title: 'General/Tags/TagList', |
||||||
|
component: TagList, |
||||||
|
decorators: [withCenteredStory], |
||||||
|
parameters: { |
||||||
|
docs: { |
||||||
|
page: mdx, |
||||||
|
}, |
||||||
|
}, |
||||||
|
}; |
||||||
|
|
||||||
|
const tags = ['datasource-test', 'gdev', 'mysql', 'mssql']; |
||||||
|
|
||||||
|
export const list = () => { |
||||||
|
return ( |
||||||
|
<div style={{ width: 300 }}> |
||||||
|
<TagList tags={tags} onClick={action('Tag clicked')} /> |
||||||
|
</div> |
||||||
|
); |
||||||
|
}; |
@ -0,0 +1,38 @@ |
|||||||
|
import React, { FC } from 'react'; |
||||||
|
import { cx, css } from 'emotion'; |
||||||
|
import { Tag } from './Tag'; |
||||||
|
|
||||||
|
export interface Props { |
||||||
|
tags: string[]; |
||||||
|
onClick?: (name: string) => any; |
||||||
|
/** Custom styles for the wrapper component */ |
||||||
|
className?: string; |
||||||
|
} |
||||||
|
|
||||||
|
export const TagList: FC<Props> = ({ tags, onClick, className }) => { |
||||||
|
const styles = getStyles(); |
||||||
|
|
||||||
|
return ( |
||||||
|
<span className={cx(styles.wrapper, className)}> |
||||||
|
{tags.map(tag => ( |
||||||
|
<Tag key={tag} name={tag} onClick={onClick} className={styles.tag} /> |
||||||
|
))} |
||||||
|
</span> |
||||||
|
); |
||||||
|
}; |
||||||
|
|
||||||
|
const getStyles = () => { |
||||||
|
return { |
||||||
|
wrapper: css` |
||||||
|
display: flex; |
||||||
|
flex: 1 1 auto; |
||||||
|
flex-wrap: wrap; |
||||||
|
padding: 10px; |
||||||
|
`,
|
||||||
|
tag: css` |
||||||
|
margin-left: 6px; |
||||||
|
font-size: 11px; |
||||||
|
padding: 2px 6px; |
||||||
|
`,
|
||||||
|
}; |
||||||
|
}; |
Loading…
Reference in new issue