confirm.stories.tsx 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. /* eslint-disable no-console */
  2. import {Fragment, useState} from 'react';
  3. import styled from '@emotion/styled';
  4. import {Button} from 'sentry/components/button';
  5. import Confirm, {openConfirmModal} from 'sentry/components/confirm';
  6. import Link from 'sentry/components/links/link';
  7. import JSXNode from 'sentry/components/stories/jsxNode';
  8. import JSXProperty from 'sentry/components/stories/jsxProperty';
  9. import Matrix from 'sentry/components/stories/matrix';
  10. import SideBySide from 'sentry/components/stories/sideBySide';
  11. import storyBook from 'sentry/stories/storyBook';
  12. import {space} from 'sentry/styles/space';
  13. export default storyBook(Confirm, story => {
  14. story('Triggers', () => (
  15. <Fragment>
  16. <p>
  17. There are two way to use <JSXNode name="Confirm" />, either as a wrapper around a
  18. trigger, or by calling <code>openConfirmModal()</code> in a callback.
  19. </p>
  20. <p>
  21. It's recommended to call <code>openConfirmModal()</code>.
  22. </p>
  23. <SideBySide>
  24. <Button onClick={() => openConfirmModal({})}>
  25. <code>{'onClick={() => openConfirmModal({})}'}</code>
  26. </Button>
  27. <Confirm>
  28. <Button>
  29. Button is wrapped with <JSXNode name="Confirm" />
  30. </Button>
  31. </Confirm>
  32. </SideBySide>
  33. </Fragment>
  34. ));
  35. story('Labels', () => (
  36. <Fragment>
  37. <p>
  38. You must implement at least <JSXProperty name="message" value={String} />, but
  39. have the option of implementing{' '}
  40. <JSXProperty name="renderMessage" value={Function} /> instead.
  41. </p>
  42. <SideBySide>
  43. <Button
  44. onClick={() =>
  45. openConfirmModal({
  46. header: 'Are you sure?',
  47. message: 'You are about to delete everything.',
  48. cancelText: 'No thanks',
  49. confirmText: 'Just do it!',
  50. priority: 'danger',
  51. })
  52. }
  53. >
  54. With String Labels
  55. </Button>
  56. <Button
  57. onClick={() =>
  58. openConfirmModal({
  59. renderMessage: _props => (
  60. <span>
  61. You are about to delete <em>Everything!</em>
  62. </span>
  63. ),
  64. renderCancelButton: ({closeModal}) => (
  65. <Link
  66. to="#"
  67. onClick={e => {
  68. closeModal();
  69. e.stopPropagation();
  70. }}
  71. >
  72. Nevermind
  73. </Link>
  74. ),
  75. renderConfirmButton: ({defaultOnClick}) => (
  76. <Button onClick={defaultOnClick}>Just do it</Button>
  77. ),
  78. })
  79. }
  80. >
  81. With ReactNode Labels
  82. </Button>
  83. </SideBySide>
  84. </Fragment>
  85. ));
  86. story('Callbacks & bypass={true}', () => {
  87. const [callbacks, setCallbacks] = useState<string[]>([]);
  88. return (
  89. <Fragment>
  90. <p>
  91. There is also a prop called <JSXProperty name="bypass" value={Boolean} />. This
  92. can help to skip the Confirm dialog, for example if not enough items are
  93. selected in a bulk-change operation, and directly run the{' '}
  94. <JSXProperty name="onConfirm" value={Function} /> callback.
  95. </p>
  96. <SideBySide>
  97. <Button
  98. onClick={() =>
  99. openConfirmModal({
  100. bypass: false,
  101. onRender: () => setCallbacks(prev => prev.concat('onRender')),
  102. onConfirming: () => setCallbacks(prev => prev.concat('onConfirming')),
  103. onCancel: () => setCallbacks(prev => prev.concat('onCancel')),
  104. onConfirm: () => setCallbacks(prev => prev.concat('onConfirm')),
  105. onClose: () => setCallbacks(prev => prev.concat('onClose')),
  106. })
  107. }
  108. >
  109. With callbacks (bypass = false)
  110. </Button>
  111. <Button
  112. onClick={() =>
  113. openConfirmModal({
  114. bypass: true,
  115. onRender: () => setCallbacks(prev => prev.concat('onRender')),
  116. onConfirming: () => setCallbacks(prev => prev.concat('onConfirming')),
  117. onCancel: () => setCallbacks(prev => prev.concat('onCancel')),
  118. onConfirm: () => setCallbacks(prev => prev.concat('onConfirm')),
  119. onClose: () => setCallbacks(prev => prev.concat('onClose')),
  120. })
  121. }
  122. >
  123. With callbacks (bypass = true)
  124. </Button>
  125. </SideBySide>
  126. <p>
  127. <label>
  128. Callback debugger:
  129. <br />
  130. <textarea rows={4} value={callbacks.join('\n')} />
  131. <br />
  132. <button onClick={() => setCallbacks([])}>Reset</button>
  133. </label>
  134. </p>
  135. </Fragment>
  136. );
  137. });
  138. story('<Confirm> child render func', () => (
  139. <Fragment>
  140. <p>
  141. Here's an example where <JSXProperty name="children" value={Function} /> is a
  142. render function:
  143. </p>
  144. <Confirm>{({open}) => <Button onClick={open}>Open the modal</Button>}</Confirm>
  145. </Fragment>
  146. ));
  147. story('<Confirm> specific props', () => {
  148. const [clicks, setClicks] = useState(0);
  149. return (
  150. <Fragment>
  151. <p>
  152. We can see how <JSXProperty name="disabled" value={Boolean} /> and
  153. <JSXProperty name="stopPropagation" value={Boolean} /> work in combination.
  154. </p>
  155. <p>Button clicks: {clicks} </p>
  156. <Matrix
  157. propMatrix={{
  158. disabled: [false, true],
  159. stopPropagation: [false, true],
  160. }}
  161. render={props => (
  162. <Button onClick={() => setClicks(prev => prev + 1)}>
  163. <Confirm {...props}>
  164. <ModalTrigger>Click the green area to open modal</ModalTrigger>
  165. </Confirm>
  166. </Button>
  167. )}
  168. selectedProps={['disabled', 'stopPropagation']}
  169. />
  170. </Fragment>
  171. );
  172. });
  173. });
  174. const ModalTrigger = styled('span')`
  175. background: ${p => p.theme.green200};
  176. padding: ${space(1)};
  177. `;