widgetFrame.stories.tsx 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. import {Fragment} from 'react';
  2. import styled from '@emotion/styled';
  3. import ExternalLink from 'sentry/components/links/externalLink';
  4. import JSXNode from 'sentry/components/stories/jsxNode';
  5. import SideBySide from 'sentry/components/stories/sideBySide';
  6. import {t, tct} from 'sentry/locale';
  7. import storyBook from 'sentry/stories/storyBook';
  8. import {WidgetFrame} from 'sentry/views/dashboards/widgets/common/widgetFrame';
  9. export default storyBook(WidgetFrame, story => {
  10. story('Getting Started', () => {
  11. return (
  12. <Fragment>
  13. <p>
  14. <JSXNode name="WidgetFrame" /> is a container element used for all widgets in
  15. the Dashboards Widget Platform. It's mostly an under-the-hood component, but it
  16. can be useful to emulate widget-like states, like widget cards with actions.
  17. </p>
  18. </Fragment>
  19. );
  20. });
  21. story('Layout', () => {
  22. return (
  23. <Fragment>
  24. <p>
  25. <JSXNode name="WidgetFrame" /> supports a few basic props that control its
  26. content. This includes a title, a description, and the <code>children</code>.
  27. The title is automatically wrapped in a tooltip if it does not fit.
  28. </p>
  29. <p>
  30. The description can be a React element, but don't go overboard. Stick to
  31. strings, or <code>tct</code> output consisting of text and links.
  32. </p>
  33. <SideBySide>
  34. <NormalWidget>
  35. <WidgetFrame
  36. title="Count"
  37. description="This counts up the amount of something that happens."
  38. />
  39. </NormalWidget>
  40. <NormalWidget>
  41. <WidgetFrame
  42. title="p95(measurements.lcp) / p95(measurements.inp)"
  43. description="This is a tough formula to reason about"
  44. />
  45. </NormalWidget>
  46. <NormalWidget>
  47. <WidgetFrame
  48. title="p95(span.duration)"
  49. description={tct('Learn more about this on our [documentation] website.', {
  50. documentation: (
  51. <ExternalLink href="https://docs.sentry.io">
  52. {t('documentation')}
  53. </ExternalLink>
  54. ),
  55. })}
  56. />
  57. </NormalWidget>
  58. </SideBySide>
  59. </Fragment>
  60. );
  61. });
  62. story('Warnings', () => {
  63. return (
  64. <Fragment>
  65. <p>
  66. <JSXNode name="WidgetFrame" /> supports a <code>warnings</code> prop. If
  67. supplied, it shows a small warning icon next to the title. Hovering over the
  68. icon shows the warnings.
  69. </p>
  70. <SideBySide>
  71. <NormalWidget>
  72. <WidgetFrame
  73. title="count()"
  74. warnings={[
  75. 'We have automatically converted this widget to use sampled data.',
  76. 'Data for this metrics has not been extracted yet',
  77. ]}
  78. />
  79. </NormalWidget>
  80. </SideBySide>
  81. </Fragment>
  82. );
  83. });
  84. story('Badge', () => {
  85. return (
  86. <Fragment>
  87. <p>
  88. <JSXNode name="WidgetFrame" /> supports a <code>badgeProps</code> prop. If
  89. passed, a<code>Badge</code> component with the relevant props appears in the
  90. header. Note: Avoid using this! This is mostly used as an internal feature, for
  91. diagnosing widget state at a glance. We might remove this feature very soon.{' '}
  92. <i>Especially</i> avoid multiple badges.
  93. </p>
  94. <SideBySide>
  95. <NormalWidget>
  96. <WidgetFrame
  97. title="count()"
  98. badgeProps={[
  99. {
  100. text: 'Alpha',
  101. type: 'alpha',
  102. },
  103. {
  104. text: 'Sampled',
  105. type: 'default',
  106. },
  107. ]}
  108. warnings={[
  109. 'We have automatically converted this widget to use sampled data.',
  110. 'Data for this metrics has not been extracted yet',
  111. ]}
  112. />
  113. </NormalWidget>
  114. </SideBySide>
  115. </Fragment>
  116. );
  117. });
  118. story('Action Menu', () => {
  119. return (
  120. <Fragment>
  121. <p>
  122. <JSXNode name="WidgetFrame" /> supports an action menu. If only one action is
  123. passed, the single action is rendered as a small button. If multiple actions are
  124. passed, they are grouped into a dropdown menu. Menu actions appear on hover or
  125. keyboard focus. They can be disabled with the <code>actionsDisabled</code> prop,
  126. and supplemented with an optional <code>actionsMessage</code> prop that adds a
  127. tooltip.
  128. </p>
  129. <SideBySide>
  130. <NormalWidget>
  131. <WidgetFrame
  132. title="Count"
  133. description="This counts up the amount of something that happens."
  134. actions={[
  135. {
  136. key: 'see-more',
  137. label: t('See More'),
  138. onAction: () => {
  139. // eslint-disable-next-line no-console
  140. console.log('See more!');
  141. },
  142. },
  143. ]}
  144. />
  145. </NormalWidget>
  146. <NormalWidget>
  147. <WidgetFrame
  148. title="Count"
  149. actionsDisabled
  150. actionsMessage="Not possible here"
  151. description="This counts up the amount of something that happens."
  152. actions={[
  153. {
  154. key: 'see-more',
  155. label: t('See More'),
  156. onAction: () => {
  157. // eslint-disable-next-line no-console
  158. console.log('See more!');
  159. },
  160. },
  161. ]}
  162. />
  163. </NormalWidget>
  164. <NormalWidget>
  165. <WidgetFrame
  166. title="Count"
  167. description="This is a tough formula to reason about"
  168. actions={[
  169. {
  170. key: 'see-more',
  171. label: t('See More'),
  172. onAction: () => {
  173. // eslint-disable-next-line no-console
  174. console.log('See more!');
  175. },
  176. },
  177. {
  178. key: 'see-less',
  179. label: t('See Less'),
  180. onAction: () => {
  181. // eslint-disable-next-line no-console
  182. console.log('See less!');
  183. },
  184. },
  185. ]}
  186. />
  187. </NormalWidget>
  188. <NormalWidget>
  189. <WidgetFrame
  190. title="Count"
  191. actionsDisabled
  192. actionsMessage="Not available in this context"
  193. actions={[
  194. {
  195. key: 'see-more',
  196. label: t('See More'),
  197. onAction: () => {
  198. // eslint-disable-next-line no-console
  199. console.log('See more!');
  200. },
  201. },
  202. {
  203. key: 'see-less',
  204. label: t('See Less'),
  205. onAction: () => {
  206. // eslint-disable-next-line no-console
  207. console.log('See less!');
  208. },
  209. },
  210. ]}
  211. />
  212. </NormalWidget>
  213. </SideBySide>
  214. </Fragment>
  215. );
  216. });
  217. story('Full Screen View Button', () => {
  218. return (
  219. <Fragment>
  220. <p>
  221. <JSXNode name="WidgetFrame" /> supports a <code>onOpenFullScreenView</code>{' '}
  222. prop. This is a special action that always appears as an individual icon to the
  223. right of the normal actions.
  224. </p>
  225. <SideBySide>
  226. <NormalWidget>
  227. <WidgetFrame title="count()" onFullScreenViewClick={() => {}} />
  228. </NormalWidget>
  229. </SideBySide>
  230. </Fragment>
  231. );
  232. });
  233. });
  234. const NormalWidget = styled('div')`
  235. width: 250px;
  236. height: 100px;
  237. `;