mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
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 previews
This commit is contained in:
parent
289a5fb862
commit
014e7d9261
23
packages/grafana-ui/src/components/Tags/Tag.mdx
Normal file
23
packages/grafana-ui/src/components/Tags/Tag.mdx
Normal file
@ -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} />
|
20
packages/grafana-ui/src/components/Tags/Tag.story.tsx
Normal file
20
packages/grafana-ui/src/components/Tags/Tag.story.tsx
Normal file
@ -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')} />;
|
||||
};
|
52
packages/grafana-ui/src/components/Tags/Tag.tsx
Normal file
52
packages/grafana-ui/src/components/Tags/Tag.tsx
Normal file
@ -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;
|
||||
}
|
||||
`,
|
||||
};
|
||||
};
|
22
packages/grafana-ui/src/components/Tags/TagList.mdx
Normal file
22
packages/grafana-ui/src/components/Tags/TagList.mdx
Normal file
@ -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} />
|
26
packages/grafana-ui/src/components/Tags/TagList.story.tsx
Normal file
26
packages/grafana-ui/src/components/Tags/TagList.story.tsx
Normal file
@ -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>
|
||||
);
|
||||
};
|
38
packages/grafana-ui/src/components/Tags/TagList.tsx
Normal file
38
packages/grafana-ui/src/components/Tags/TagList.tsx
Normal file
@ -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;
|
||||
`,
|
||||
};
|
||||
};
|
@ -41,6 +41,8 @@ export { TimeOfDayPicker } from './TimePicker/TimeOfDayPicker';
|
||||
export { List } from './List/List';
|
||||
export { TagsInput } from './TagsInput/TagsInput';
|
||||
export { Pagination } from './Pagination/Pagination';
|
||||
export { Tag } from './Tags/Tag';
|
||||
export { TagList } from './Tags/TagList';
|
||||
|
||||
export { ConfirmModal } from './ConfirmModal/ConfirmModal';
|
||||
export { QueryField } from './QueryField/QueryField';
|
||||
|
Loading…
Reference in New Issue
Block a user