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 { List } from './List/List';
|
||||||
export { TagsInput } from './TagsInput/TagsInput';
|
export { TagsInput } from './TagsInput/TagsInput';
|
||||||
export { Pagination } from './Pagination/Pagination';
|
export { Pagination } from './Pagination/Pagination';
|
||||||
|
export { Tag } from './Tags/Tag';
|
||||||
|
export { TagList } from './Tags/TagList';
|
||||||
|
|
||||||
export { ConfirmModal } from './ConfirmModal/ConfirmModal';
|
export { ConfirmModal } from './ConfirmModal/ConfirmModal';
|
||||||
export { QueryField } from './QueryField/QueryField';
|
export { QueryField } from './QueryField/QueryField';
|
||||||
|
Loading…
Reference in New Issue
Block a user