notification.stories.tsx 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437
  1. import fetchMock from 'fetch-mock'
  2. import Notification from '../../js/shared/components/notification'
  3. import { postJSON } from '../../js/infrastructure/fetch-json'
  4. import useAsync from '../../js/shared/hooks/use-async'
  5. import { figmaDesignUrl } from '../../../.storybook/utils/figma-design-url'
  6. type Args = React.ComponentProps<typeof Notification>
  7. export const NotificationInfo = (args: Args) => {
  8. return <Notification {...args} isDismissible />
  9. }
  10. NotificationInfo.parameters = figmaDesignUrl(
  11. 'https://www.figma.com/design/V7Ogph1Ocs4ux2A4WMNAh7/Overleaf---Components?node-id=3488-103958&m=dev'
  12. )
  13. export const NotificationSuccess = (args: Args) => {
  14. return <Notification {...args} isDismissible type="success" />
  15. }
  16. NotificationSuccess.parameters = figmaDesignUrl(
  17. 'https://www.figma.com/design/V7Ogph1Ocs4ux2A4WMNAh7/Overleaf---Components?node-id=3489-110077&m=dev'
  18. )
  19. export const NotificationWarning = (args: Args) => {
  20. return <Notification {...args} isDismissible type="warning" />
  21. }
  22. NotificationWarning.parameters = figmaDesignUrl(
  23. 'https://www.figma.com/design/V7Ogph1Ocs4ux2A4WMNAh7/Overleaf---Components?node-id=3489-112036&m=dev'
  24. )
  25. export const NotificationError = (args: Args) => {
  26. return <Notification {...args} isDismissible type="error" />
  27. }
  28. NotificationError.parameters = figmaDesignUrl(
  29. 'https://www.figma.com/design/V7Ogph1Ocs4ux2A4WMNAh7/Overleaf---Components?node-id=3489-112059&m=dev'
  30. )
  31. export const NotificationOffer = (args: Args) => {
  32. return <Notification {...args} isDismissible type="offer" />
  33. }
  34. export const NotificationWithActionBelowContent = (args: Args) => {
  35. return (
  36. <Notification
  37. {...args}
  38. content={
  39. <div>
  40. <p>The CTA will always go below the content on small screens.</p>
  41. <p>
  42. We can also opt to always put the CTA below the content on all
  43. screens
  44. </p>
  45. </div>
  46. }
  47. isDismissible
  48. isActionBelowContent
  49. />
  50. )
  51. }
  52. NotificationWithActionBelowContent.parameters = figmaDesignUrl(
  53. 'https://www.figma.com/design/V7Ogph1Ocs4ux2A4WMNAh7/Overleaf---Components?node-id=3489-118816&m=dev'
  54. )
  55. export const NotificationWithTitle = (args: Args) => {
  56. return <Notification {...args} title="Some title" />
  57. }
  58. NotificationWithTitle.parameters = figmaDesignUrl(
  59. 'https://www.figma.com/design/V7Ogph1Ocs4ux2A4WMNAh7/Overleaf---Components?node-id=3489-115045&m=dev'
  60. )
  61. export const NotificationWithAction = (args: Args) => {
  62. return <Notification {...args} isDismissible={false} />
  63. }
  64. NotificationWithAction.parameters = figmaDesignUrl(
  65. 'https://www.figma.com/design/V7Ogph1Ocs4ux2A4WMNAh7/Overleaf---Components?node-id=3489-117190&m=dev'
  66. )
  67. export const NotificationDismissible = (args: Args) => {
  68. return <Notification {...args} action={undefined} />
  69. }
  70. NotificationDismissible.parameters = figmaDesignUrl(
  71. 'https://www.figma.com/design/V7Ogph1Ocs4ux2A4WMNAh7/Overleaf---Components?node-id=3489-116677&m=dev'
  72. )
  73. export const APlainNotification = (args: Args) => {
  74. return <Notification {...args} action={undefined} isDismissible={false} />
  75. }
  76. export const NotificationWithMultipleParagraphsAndActionAndDismissible = (
  77. args: Args
  78. ) => {
  79. return (
  80. <Notification
  81. {...args}
  82. content={
  83. <div>
  84. <p>
  85. <b>Lorem ipsum</b>
  86. </p>
  87. <p>
  88. Dolor sit amet, consectetur adipiscing elit. Proin lacus velit,
  89. faucibus vitae feugiat sit amet, <a href="/">Some link</a> iaculis
  90. ut mi.
  91. </p>
  92. <p>
  93. Vel eros donec ac odio tempor orci dapibus ultrices in. Fermentum
  94. iaculis eu non diam phasellus.
  95. </p>
  96. <p>Aliquam at tempor risus. Vestibulum bibendum ut </p>
  97. </div>
  98. }
  99. />
  100. )
  101. }
  102. export const NotificationWithMultipleParagraphsAndDismissible = (
  103. args: Args
  104. ) => {
  105. return (
  106. <Notification
  107. {...args}
  108. action={undefined}
  109. content={
  110. <div>
  111. <p>
  112. <b>Lorem ipsum</b>
  113. </p>
  114. <p>
  115. Dolor sit amet, consectetur adipiscing elit. Proin lacus velit,
  116. faucibus vitae feugiat sit amet, <a href="/">Some link</a> iaculis
  117. ut mi.
  118. </p>
  119. <p>
  120. Vel eros donec ac odio tempor orci dapibus ultrices in. Fermentum
  121. iaculis eu non diam phasellus.
  122. </p>
  123. <p>Aliquam at tempor risus. Vestibulum bibendum ut </p>
  124. </div>
  125. }
  126. />
  127. )
  128. }
  129. export const MultipleParagraphsAndAction = (args: Args) => {
  130. return (
  131. <Notification
  132. {...args}
  133. isDismissible={false}
  134. content={
  135. <div>
  136. <p>
  137. <b>Lorem ipsum</b>
  138. </p>
  139. <p>
  140. Dolor sit amet, consectetur adipiscing elit. Proin lacus velit,
  141. faucibus vitae feugiat sit amet, <a href="/">Some link</a> iaculis
  142. ut mi.
  143. </p>
  144. <p>
  145. Vel eros donec ac odio tempor orci dapibus ultrices in. Fermentum
  146. iaculis eu non diam phasellus.
  147. </p>
  148. <p>Aliquam at tempor risus. Vestibulum bibendum ut </p>
  149. </div>
  150. }
  151. />
  152. )
  153. }
  154. export const MultipleParagraphs = (args: Args) => {
  155. return (
  156. <Notification
  157. {...args}
  158. action={undefined}
  159. isDismissible={false}
  160. content={
  161. <div>
  162. <p>
  163. <b>Lorem ipsum</b>
  164. </p>
  165. <p>
  166. Dolor sit amet, consectetur adipiscing elit. Proin lacus velit,
  167. faucibus vitae feugiat sit amet, <a href="/">Some link</a> iaculis
  168. ut mi.
  169. </p>
  170. <p>
  171. Vel eros donec ac odio tempor orci dapibus ultrices in. Fermentum
  172. iaculis eu non diam phasellus.
  173. </p>
  174. <p>Aliquam at tempor risus. Vestibulum bibendum ut </p>
  175. </div>
  176. }
  177. />
  178. )
  179. }
  180. export const ShortText = (args: Args) => {
  181. return (
  182. <Notification
  183. {...args}
  184. action={undefined}
  185. isDismissible={false}
  186. content={<p>Lorem ipsum</p>}
  187. />
  188. )
  189. }
  190. export const ShortTextAndDismissible = (args: Args) => {
  191. return (
  192. <Notification {...args} action={undefined} content={<p>Lorem ipsum</p>} />
  193. )
  194. }
  195. export const ShortTextAndActionLinkAsButton = (args: Args) => {
  196. return (
  197. <Notification
  198. {...args}
  199. isDismissible={false}
  200. content={<p>Lorem ipsum</p>}
  201. />
  202. )
  203. }
  204. export const ShortTextAndActionAsLink = (args: Args) => {
  205. return (
  206. <Notification
  207. {...args}
  208. content={<p>Lorem ipsum</p>}
  209. action={<a href="/">An action</a>}
  210. isDismissible={false}
  211. />
  212. )
  213. }
  214. export const ShortTextAndActionAsLinkButStyledAsButton = (args: Args) => {
  215. return (
  216. <Notification
  217. {...args}
  218. content={<p>Lorem ipsum</p>}
  219. action={
  220. <a href="/" className="btn btn-secondary">
  221. An action
  222. </a>
  223. }
  224. isDismissible={false}
  225. />
  226. )
  227. }
  228. export const LongActionButton = (args: Args) => {
  229. return (
  230. <Notification
  231. {...args}
  232. action={
  233. <button className="btn btn-secondary">
  234. Action that has a lot of text
  235. </button>
  236. }
  237. />
  238. )
  239. }
  240. export const LongActionLink = (args: Args) => {
  241. return (
  242. <Notification
  243. {...args}
  244. action={<a href="/">Action that has a lot of text</a>}
  245. />
  246. )
  247. }
  248. export const CustomIcon = (args: Args) => {
  249. return (
  250. <Notification
  251. {...args}
  252. customIcon={<div style={{ marginTop: '-4px' }}>🎉</div>}
  253. />
  254. )
  255. }
  256. export const MultipleButtons = (args: Args) => {
  257. return (
  258. <Notification
  259. {...args}
  260. content={<p>Lorem ipsum</p>}
  261. action={
  262. <>
  263. <button className="btn btn-secondary">button1</button>
  264. <button className="btn btn-secondary">button2</button>
  265. </>
  266. }
  267. type="info"
  268. isActionBelowContent
  269. isDismissible
  270. />
  271. )
  272. }
  273. export const OverlayedWithCustomClass = (args: Args) => {
  274. return (
  275. <>
  276. <Notification
  277. {...args}
  278. content={
  279. <p>
  280. This can be <b>any HTML</b> passed to the component. For example,
  281. paragraphs, headers, <code>code samples</code>,{' '}
  282. <a href="/">links</a>, etc are all supported.
  283. </p>
  284. }
  285. className="ol-overlay"
  286. action={
  287. <>
  288. <button className="btn btn-secondary">button1</button>
  289. <button className="btn btn-secondary">button2</button>
  290. </>
  291. }
  292. type="info"
  293. isActionBelowContent
  294. isDismissible
  295. />
  296. <div>
  297. <p>we need filler content, so here are some jokes</p>
  298. <ul>
  299. <li>Did you hear about the circus fire? It was in tents!</li>
  300. <li>How do you catch a squirrel? Climb a tree and act like a nut!</li>
  301. <li>
  302. Did you hear about the guy who invented Lifesavers? They say he made
  303. a mint!
  304. </li>
  305. <li>
  306. Why couldn't the bicycle stand up by itself? It was two tired.
  307. </li>
  308. <li>
  309. did one hat say to the other?" "Stay here! I'm going on ahead.
  310. </li>
  311. <li>
  312. Why did Billy get fired from the banana factory? He kept throwing
  313. away the bent ones.
  314. </li>
  315. </ul>
  316. </div>
  317. </>
  318. )
  319. }
  320. export const SuccessFlow = (args: Args) => {
  321. console.log('.....render')
  322. fetchMock.post('express:/test-success', { status: 200 }, { delay: 250 })
  323. const { isLoading, isSuccess, runAsync } = useAsync()
  324. function handleClick() {
  325. console.log('clicked')
  326. runAsync(postJSON('/test-success')).catch(console.error)
  327. }
  328. const ctaText = isLoading ? 'Processing' : 'Click'
  329. const action = (
  330. <button
  331. className="btn btn-secondary"
  332. onClick={() => handleClick()}
  333. disabled={isLoading}
  334. >
  335. {ctaText}
  336. </button>
  337. )
  338. const startNotification = (
  339. <Notification
  340. {...args}
  341. action={action}
  342. title="An example notification flow"
  343. content={
  344. <p>
  345. This story shows 2 notifications, and it's up to the parent component
  346. to determine which to show. There's a successful request made after
  347. clicking the action and so the parent component then renders the
  348. success notification.
  349. </p>
  350. }
  351. />
  352. )
  353. const successNotification = (
  354. <Notification
  355. {...args}
  356. action={<a href="/">Now follow this link to go home</a>}
  357. type="success"
  358. content={<p>Success! You made a successful request.</p>}
  359. />
  360. )
  361. if (isSuccess) return successNotification
  362. return startNotification
  363. }
  364. export const ContentAsAString = (args: Args) => {
  365. return <Notification {...args} content="An alert" />
  366. }
  367. export default {
  368. title: 'Shared / Components / Notification',
  369. component: Notification,
  370. args: {
  371. content: (
  372. <p>
  373. This can be <b>any HTML</b> passed to the component. For example,
  374. paragraphs, headers, <code>code samples</code>, <a href="/">links</a>,
  375. etc are all supported.
  376. </p>
  377. ),
  378. action: <button className="btn btn-secondary">An action</button>,
  379. isDismissible: true,
  380. title: undefined,
  381. },
  382. argTypes: {
  383. content: {
  384. control: 'text',
  385. },
  386. action: {
  387. control: 'text',
  388. },
  389. title: {
  390. control: 'text',
  391. },
  392. },
  393. parameters: {
  394. controls: {
  395. include: [
  396. 'content',
  397. 'title',
  398. 'action',
  399. 'ariaLive',
  400. 'type',
  401. 'isDismissible',
  402. 'isActionBelowContent',
  403. ],
  404. },
  405. },
  406. }