diff --git a/.betterer.ts b/.betterer.ts
index e6e3a066777..7439cf7fa87 100644
--- a/.betterer.ts
+++ b/.betterer.ts
@@ -31,9 +31,16 @@ function countUndocumentedStories() {
await Promise.all(
filePaths.map(async (filePath) => {
// look for .mdx import in the story file
- const regex = new RegExp("^import.*.mdx';$", 'gm');
+ const mdxImportRegex = new RegExp("^import.*\\.mdx';$", 'gm');
+ // Looks for the "autodocs" string in the file
+ const autodocsStringRegex = /autodocs/;
+
const fileText = await fs.readFile(filePath, 'utf8');
- if (!regex.test(fileText)) {
+
+ const hasMdxImport = mdxImportRegex.test(fileText);
+ const hasAutodocsString = autodocsStringRegex.test(fileText);
+ // If both .mdx import and autodocs string are missing, add an issue
+ if (!hasMdxImport && !hasAutodocsString) {
// In this case the file contents don't matter:
const file = fileTestResult.addFile(filePath, '');
// Add the issue to the first character of the file:
diff --git a/packages/grafana-ui/.storybook/preview.ts b/packages/grafana-ui/.storybook/preview.ts
index effdab3a18f..e58cfed7230 100644
--- a/packages/grafana-ui/.storybook/preview.ts
+++ b/packages/grafana-ui/.storybook/preview.ts
@@ -50,6 +50,10 @@ const preview: Preview = {
// We should be able to use the builtin alphabetical sort, but is broken in SB 7.0
// https://github.com/storybookjs/storybook/issues/22470
storySort: (a, b) => {
+ // Skip sorting for stories with nosort tag
+ if (a.tags.includes('nosort') || b.tags.includes('nosort')) {
+ return 0;
+ }
if (a.title.startsWith('Docs Overview')) {
if (b.title.startsWith('Docs Overview')) {
return 0;
diff --git a/packages/grafana-ui/src/components/Card/Card.mdx b/packages/grafana-ui/src/components/Card/Card.mdx
deleted file mode 100644
index 2e604d527f7..00000000000
--- a/packages/grafana-ui/src/components/Card/Card.mdx
+++ /dev/null
@@ -1,450 +0,0 @@
-import { Meta, Preview, ArgTypes } from '@storybook/blocks';
-import { Card } from './Card';
-import { Button } from '../Button';
-import { IconButton } from '../IconButton/IconButton';
-import { TagList } from '../Tags/TagList';
-
-export const logo = 'https://grafana.com/static/assets/img/apple-touch-icon.png';
-
-
-
-# Card
-
-## Usage
-
-### Basic
-
-A basic `Card` component expects at least a heading, used as a title.
-
-```jsx
-
- Filter by name
- Filter data by query.
-
-```
-
-
-
- Filter by name
- Filter data by query.
-
-
-
-### Multiple metadata elements
-
-For providing metadata elements, which can be any extra information for the card, `Card.Meta` component should be used. If metadata consists of multiple strings, each of them has to be escaped (wrapped in brackets `{}`) or better passed in as an array.
-
-```jsx
-
- Test dashboard
- {['Folder: Test', 'Views: 100']}
-
-```
-
-
-
- Test dashboard
- {['Folder: Test', 'Views: 100']}
-
-
-
-Metadata also accepts HTML elements, which could be links, for example. For elements, that are not strings, a `key` prop has to be manually specified.
-
-```jsx
-
- Test dashboard
-
- Grafana
-
- https://ops-us-east4.grafana.net/api/prom
-
-
-
-```
-
-
-
- Test dashboard
-
- Grafana
-
- https://ops-us-east4.grafana.net/api/prom
-
-
-
-
-
-The separator for multiple metadata elements defaults to a vertical line `|`, but can be customised.
-
-```jsx
-
- Test dashboard
-
- Grafana
-
- https://ops-us-east4.grafana.net/api/prom
-
-
-
-```
-
-
-
- Test dashboard
-
- Grafana
-
- https://ops-us-east4.grafana.net/api/prom
-
-
-
-
-
-### Tags
-
-Tags can be rendered inside the Card, by being wrapped in `Card.Tags` component. Note that this component does not provide any tag styling and that should be handled by the children. It is recommended to use it with Grafana-UI's `TagList` component.
-
-```jsx
-
- Test dashboard
- Card with a list of tags
-
- console.log(tag)} />
-
-
-```
-
-
-
- Test dashboard
- Card with a list of tags
-
- console.log(tag)} />
-
-
-
-
-### As a link
-
-Card can be used as a clickable link item by specifying `href` prop.
-
-```jsx
-
- Redirect to Grafana
- Clicking this card will redirect to grafana website
-
-```
-
-
-
- Redirect to Grafana
- Clicking this card will redirect to grafana website
-
-
-
-### As a button
-
-Card can be used as a clickable buttons item by specifying `onClick` prop.
-
-```jsx
- alert('Hello, Grafana!')}>
- Hello, Grafana
- Clicking this card will create an alert
-
-```
-
-
- alert('Hello, Grafana!')}>
- Hello, Grafana
- Clicking this card will create an alert
-
-
-
-> **Note**: When used in conjunction with [Metadata elements](#multiple-metadata-elements), clicking on any element
-> inside `` will prevent the card action to be executed (either `href` to be followed or `onClick` to be called).
->
-> Example:
-
-```jsx
- alert('Hello, Grafana!')}>
- Hello, Grafana
- Clicking on this text (Meta) WILL NOT trigger the alert!
- Clicking on this text (Description) WILL trigger the alert!
-
-```
-
-
- alert('Hello, Grafana!')}>
- Hello, Grafana
- Clicking on this text (Meta) WILL NOT trigger the alert!
- Clicking on this text (Description) WILL trigger the alert!
-
-
-
-### Inside a list item
-
-To render cards in a list, it is possible to nest them inside `li` items.
-
-```jsx
-
-
-
- List card item
- Card that is rendered inside li element.
-
-
-
-
- List card item
- Card that is rendered inside li element.
-
-
-
-
- List card item
- Card that is rendered inside li element.
-
-
-
-
- List card item
- Card that is rendered inside li element.
-
-
-
-```
-
-
-
-
-
- List card item
- Card that is rendered inside li element.
-
-
-
-
- List card item
- Card that is rendered inside li element.
-
-
-
-
- List card item
- Card that is rendered inside li element.
-
-
-
-
- List card item
- Card that is rendered inside li element.
-
-
-
-
-
-### With media elements
-
-Cards can also be rendered with media content such icons or images. Such elements need to be wrapped in `Card.Figure` component.
-
-```jsx
-
- 1-ops-tools1-fallback
-
-
-
-
- Grafana
-
- https://ops-us-east4.grafana.net/api/prom
-
-
-
-```
-
-
-
- 1-ops-tools1-fallback
-
-
-
-
- Grafana
-
- https://ops-us-east4.grafana.net/api/prom
-
-
-
-
-
-### Action Cards
-
-Cards also accept primary and secondary actions. Usually the primary actions are displayed as buttons while secondary actions are displayed as icon buttons. The actions need to be wrappd in `Card.Actions` and `Card.SecondaryActions` components respectively.
-
-```jsx
-
- 1-ops-tools1-fallback
-
- Grafana
-
- https://ops-us-east4.grafana.net/api/prom
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-```
-
-
-
- 1-ops-tools1-fallback
-
- Grafana
-
- https://ops-us-east4.grafana.net/api/prom
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-### Disabled state
-
-Card can have a disabled state, effectively making it and its actions non-clickable. If there are any actions, they will be disabled instead of the whole card.
-
-```jsx
-
- 1-ops-tools1-fallback
-
- Grafana
-
- https://ops-us-east4.grafana.net/api/prom
-
-
-
-
-
-
-```
-
-
-
- 1-ops-tools1-fallback
-
- Grafana
-
- https://ops-us-east4.grafana.net/api/prom
-
-
-
-
-
-
-
-
-```jsx
-
- 1-ops-tools1-fallback
-
- Grafana
-
- https://ops-us-east4.grafana.net/api/prom
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-```
-
-
-
- 1-ops-tools1-fallback
-
- Grafana
-
- https://ops-us-east4.grafana.net/api/prom
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-### Selectable
-
-```jsx
-
- Option #1
- This is a really great option, you won't regret it.
-
-
-
-
-```
-
-
-
- Option #1
- This is a really great option, you won't regret it.
-
-
-
-
-
-
-### Props
-
-
diff --git a/packages/grafana-ui/src/components/Card/Card.story.tsx b/packages/grafana-ui/src/components/Card/Card.story.tsx
index e54bed30b77..c53e01798b0 100644
--- a/packages/grafana-ui/src/components/Card/Card.story.tsx
+++ b/packages/grafana-ui/src/components/Card/Card.story.tsx
@@ -3,27 +3,27 @@ import React from 'react';
import { Button } from '../Button';
import { IconButton } from '../IconButton/IconButton';
-import { VerticalGroup } from '../Layout/Layout';
import { TagList } from '../Tags/TagList';
import { Card } from './Card';
-import mdx from './Card.mdx';
const logo = 'https://grafana.com/static/assets/img/apple-touch-icon.png';
const meta: Meta = {
title: 'General/Card',
component: Card,
+ // nosort is a custom tag used so the stories shown in docs keep the order they are defined in the file
+ tags: ['autodocs', 'nosort'],
parameters: {
- docs: {
- page: mdx,
- },
controls: {
exclude: ['onClick', 'href', 'heading', 'description', 'className'],
},
},
};
+/**
+ * A basic Card component expects at least a heading, used as a title.
+ */
export const Basic: StoryFn = (args) => {
return (
@@ -36,60 +36,157 @@ export const Basic: StoryFn = (args) => {
);
};
-export const AsLink: StoryFn = (args) => {
+/**
+ * For providing metadata elements, which can be any extra information for the card, Card.Meta component should be used.
+ * If metadata consists of multiple strings, each of them has to be escaped (wrapped in brackets {}) or better passed in as an array.
+ */
+export const MultipleMetadataElements: StoryFn = (args) => {
return (
-
-
- Filter by name
-
- Filter data by query. This is useful if you are sharing the results from a different panel that has many
- queries and you want to only visualize a subset of that in this panel.
-
-
-
- Filter by name2
-
- Filter data by query. This is useful if you are sharing the results from a different panel that has many
- queries and you want to only visualize a subset of that in this panel.
-
-
-
- Production system overview
- Meta tags
-
-
+
+ Test dashboard
+ {['Folder: Test', 'Views: 100']}
+
);
};
-export const WithTags: StoryFn = (args) => {
+/**
+ * Metadata also accepts HTML elements, which could be links, for example.
+ * For elements, that are not strings, a `key` prop has to be manually specified.
+ */
+export const ComplexMetadataElements: StoryFn = (args) => {
return (
-
- Elasticsearch – Custom Templated Query
- Elastic Search
+
+ Test dashboard
+
+ <>Grafana>
+
+ <>https://ops-us-east4.grafana.net/api/prom>
+
+
+
+ );
+};
+
+/**
+ * The separator for multiple metadata elements defaults to a vertical line `|`, but can be customised.
+ */
+export const MultipleMetadataWithCustomSeparator: StoryFn = (args) => {
+ return (
+
+ Test dashboard
+
+ Grafana
+
+ https://ops-us-east4.grafana.net/api/prom
+
+
+
+ );
+};
+
+/**
+ * Tags can be rendered inside the Card, by being wrapped in `Card.Tags` component.
+ * Note that this component does not provide any tag styling and that should be handled by the children.
+ * It is recommended to use it with Grafana-UI's `TagList` component.
+ */
+export const Tags: StoryFn = (args) => {
+ return (
+
+ Test dashboard
+ Card with a list of tags
- console.log('tag', tag)} />
+ console.log(tag)} />
);
};
-export const WithMedia: StoryFn = (args) => {
+/**
+ * Card can be used as a clickable link item by specifying `href` prop.
+ */
+export const AsALink: StoryFn = (args) => {
return (
-
- 1-ops-tools1-fallback
-
- Prometheus
-
- https://ops-us-east4.grafana.net/api/prom
-
-
-
-
-
+
+ Redirect to Grafana
+ Clicking this card will redirect to grafana website
);
};
-export const WithActions: StoryFn = (args) => {
+
+/**
+ * Card can be used as a clickable buttons item by specifying `onClick` prop.
+ * **Note:** When used in conjunction with [Metadata elements](#multiple-metadata-elements), clicking on any element
+ * inside `` will prevent the card action to be executed (either `href` to be followed or `onClick` to be called).
+ */
+export const AsAButton: StoryFn = (args) => {
+ return (
+ alert('Hello, Grafana!')}>
+ Hello, Grafana
+ Clicking this card will create an alert
+
+ );
+};
+
+/**
+ * To render cards in a list, it is possible to nest them inside `li` items.
+ */
+export const InsideAListItem: StoryFn = (args) => {
+ return (
+
+
+
+ List card item
+ Card that is rendered inside li element.
+
+
+
+
+ List card item
+ Card that is rendered inside li element.
+
+
+
+
+ List card item
+ Card that is rendered inside li element.
+
+
+
+
+ List card item
+ Card that is rendered inside li element.
+
+
+
+ );
+};
+
+/**
+ * Cards can also be rendered with media content such icons or images. Such elements need to be wrapped in `Card.Figure` component.
+ */
+export const WithMediaElements: StoryFn = (args) => {
+ return (
+
+ 1-ops-tools1-fallback
+
+
+
+
+ Grafana
+
+ https://ops-us-east4.grafana.net/api/prom
+
+
+
+ );
+};
+
+/**
+ * Cards also accept primary and secondary actions. Usually the primary actions are displayed as buttons
+ * while secondary actions are displayed as icon buttons. The actions need to be wrapped in `Card.Actions`
+ * and `Card.SecondaryActions` components respectively.
+ */
+export const ActionCards: StoryFn = (args) => {
return (
1-ops-tools1-fallback
@@ -118,6 +215,51 @@ export const WithActions: StoryFn = (args) => {
);
};
+/**
+ * Card can have a disabled state, effectively making it and its actions non-clickable.
+ * If there are any actions, they will be disabled instead of the whole card.
+ */
+export const DisabledState: StoryFn = (args) => {
+ return (
+
+ 1-ops-tools1-fallback
+
+ Grafana
+
+ https://ops-us-east4.grafana.net/api/prom
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export const Selectable: StoryFn = () => {
+ return (
+
+ Option #1
+ This is a really great option, you will not regret it.
+
+
+
+
+ );
+};
+
export const Full: StoryFn = (args) => {
return (
@@ -155,28 +297,4 @@ export const Full: StoryFn = (args) => {
);
};
-export const Selected: StoryFn = () => {
- return (
-
- Spaces
- Spaces are the superior form of indenting code.
-
-
-
-
- );
-};
-
-export const NotSelected: StoryFn = () => {
- return (
-
- Tabs
- Tabs are the preferred way of indentation.
-
-
-
-
- );
-};
-
export default meta;