@atlaskit/modal-dialog 12.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. package/CHANGELOG.md +2111 -0
  2. package/LICENSE +13 -0
  3. package/README.md +13 -0
  4. package/__perf__/default.tsx +42 -0
  5. package/__perf__/interactions.tsx +136 -0
  6. package/__perf__/scroll.tsx +98 -0
  7. package/codemods/12.0.0-lite-mode.ts +51 -0
  8. package/codemods/__tests__/12.0.0-lite-mode.test.ts +493 -0
  9. package/codemods/__tests__/handle-prop-spread.tsx +276 -0
  10. package/codemods/__tests__/inline-WidthNames-declaration.test.ts +260 -0
  11. package/codemods/__tests__/map-actions-prop.tsx +436 -0
  12. package/codemods/__tests__/map-body-from-props.test.ts +645 -0
  13. package/codemods/__tests__/map-container-from-props.test.ts +323 -0
  14. package/codemods/__tests__/map-footer-from-props.test.ts +544 -0
  15. package/codemods/__tests__/map-header-from-props.test.ts +559 -0
  16. package/codemods/__tests__/map-heading-prop.tsx +438 -0
  17. package/codemods/__tests__/remove-appearance-prop.test.ts +79 -0
  18. package/codemods/__tests__/remove-component-override-props.test.ts +153 -0
  19. package/codemods/__tests__/remove-is-chromeless.tsx +182 -0
  20. package/codemods/__tests__/rename-appearance-type.test.ts +52 -0
  21. package/codemods/__tests__/rename-inner-component-prop-types.test.ts +82 -0
  22. package/codemods/__tests__/rename-scrollBehavior-to-shouldScrollInViewport.test.ts +237 -0
  23. package/codemods/internal/constants.tsx +41 -0
  24. package/codemods/internal/utils.tsx +223 -0
  25. package/codemods/migrations/handle-prop-spread.tsx +51 -0
  26. package/codemods/migrations/inline-WidthNames-declaration.ts +92 -0
  27. package/codemods/migrations/map-actions-prop.tsx +430 -0
  28. package/codemods/migrations/map-body-from-props.ts +147 -0
  29. package/codemods/migrations/map-container-from-props.ts +72 -0
  30. package/codemods/migrations/map-footer-from-props.ts +107 -0
  31. package/codemods/migrations/map-header-from-props.ts +101 -0
  32. package/codemods/migrations/map-heading-prop.tsx +193 -0
  33. package/codemods/migrations/remove-appearance-prop.ts +27 -0
  34. package/codemods/migrations/remove-component-override-props.ts +84 -0
  35. package/codemods/migrations/remove-is-chromeless.tsx +42 -0
  36. package/codemods/migrations/rename-appearance-type.ts +9 -0
  37. package/codemods/migrations/rename-inner-component-prop-types.ts +28 -0
  38. package/codemods/migrations/rename-scrollBehavior-to-shouldScrollInViewport.ts +82 -0
  39. package/dist/cjs/hooks.js +22 -0
  40. package/dist/cjs/index.js +63 -0
  41. package/dist/cjs/internal/components/modal-dialog.js +155 -0
  42. package/dist/cjs/internal/components/positioner.js +89 -0
  43. package/dist/cjs/internal/components/scroll-container.js +138 -0
  44. package/dist/cjs/internal/constants.js +48 -0
  45. package/dist/cjs/internal/context.js +13 -0
  46. package/dist/cjs/internal/hooks/use-modal-stack.js +110 -0
  47. package/dist/cjs/internal/hooks/use-on-motion-finish.js +24 -0
  48. package/dist/cjs/internal/hooks/use-prevent-programmatic-scroll.js +55 -0
  49. package/dist/cjs/internal/hooks/use-scroll.js +20 -0
  50. package/dist/cjs/internal/utils.js +35 -0
  51. package/dist/cjs/modal-body.js +66 -0
  52. package/dist/cjs/modal-footer.js +40 -0
  53. package/dist/cjs/modal-header.js +43 -0
  54. package/dist/cjs/modal-title.js +108 -0
  55. package/dist/cjs/modal-transition.js +21 -0
  56. package/dist/cjs/modal-wrapper.js +126 -0
  57. package/dist/cjs/types.js +5 -0
  58. package/dist/cjs/version.json +5 -0
  59. package/dist/es2019/hooks.js +11 -0
  60. package/dist/es2019/index.js +7 -0
  61. package/dist/es2019/internal/components/modal-dialog.js +120 -0
  62. package/dist/es2019/internal/components/positioner.js +78 -0
  63. package/dist/es2019/internal/components/scroll-container.js +97 -0
  64. package/dist/es2019/internal/constants.js +27 -0
  65. package/dist/es2019/internal/context.js +3 -0
  66. package/dist/es2019/internal/hooks/use-modal-stack.js +85 -0
  67. package/dist/es2019/internal/hooks/use-on-motion-finish.js +17 -0
  68. package/dist/es2019/internal/hooks/use-prevent-programmatic-scroll.js +39 -0
  69. package/dist/es2019/internal/hooks/use-scroll.js +11 -0
  70. package/dist/es2019/internal/utils.js +22 -0
  71. package/dist/es2019/modal-body.js +50 -0
  72. package/dist/es2019/modal-footer.js +30 -0
  73. package/dist/es2019/modal-header.js +30 -0
  74. package/dist/es2019/modal-title.js +94 -0
  75. package/dist/es2019/modal-transition.js +10 -0
  76. package/dist/es2019/modal-wrapper.js +88 -0
  77. package/dist/es2019/types.js +1 -0
  78. package/dist/es2019/version.json +5 -0
  79. package/dist/esm/hooks.js +11 -0
  80. package/dist/esm/index.js +7 -0
  81. package/dist/esm/internal/components/modal-dialog.js +131 -0
  82. package/dist/esm/internal/components/positioner.js +76 -0
  83. package/dist/esm/internal/components/scroll-container.js +114 -0
  84. package/dist/esm/internal/constants.js +27 -0
  85. package/dist/esm/internal/context.js +3 -0
  86. package/dist/esm/internal/hooks/use-modal-stack.js +96 -0
  87. package/dist/esm/internal/hooks/use-on-motion-finish.js +16 -0
  88. package/dist/esm/internal/hooks/use-prevent-programmatic-scroll.js +44 -0
  89. package/dist/esm/internal/hooks/use-scroll.js +11 -0
  90. package/dist/esm/internal/utils.js +22 -0
  91. package/dist/esm/modal-body.js +49 -0
  92. package/dist/esm/modal-footer.js +29 -0
  93. package/dist/esm/modal-header.js +29 -0
  94. package/dist/esm/modal-title.js +93 -0
  95. package/dist/esm/modal-transition.js +10 -0
  96. package/dist/esm/modal-wrapper.js +96 -0
  97. package/dist/esm/types.js +1 -0
  98. package/dist/esm/version.json +5 -0
  99. package/dist/types/hooks.d.ts +1 -0
  100. package/dist/types/index.d.ts +8 -0
  101. package/dist/types/internal/components/modal-dialog.d.ts +3 -0
  102. package/dist/types/internal/components/positioner.d.ts +10 -0
  103. package/dist/types/internal/components/scroll-container.d.ts +20 -0
  104. package/dist/types/internal/constants.d.ts +25 -0
  105. package/dist/types/internal/context.d.ts +20 -0
  106. package/dist/types/internal/hooks/use-modal-stack.d.ts +13 -0
  107. package/dist/types/internal/hooks/use-on-motion-finish.d.ts +4 -0
  108. package/dist/types/internal/hooks/use-prevent-programmatic-scroll.d.ts +7 -0
  109. package/dist/types/internal/hooks/use-scroll.d.ts +1 -0
  110. package/dist/types/internal/utils.d.ts +3 -0
  111. package/dist/types/modal-body.d.ts +16 -0
  112. package/dist/types/modal-footer.d.ts +16 -0
  113. package/dist/types/modal-header.d.ts +16 -0
  114. package/dist/types/modal-title.d.ts +26 -0
  115. package/dist/types/modal-transition.d.ts +3 -0
  116. package/dist/types/modal-wrapper.d.ts +5 -0
  117. package/dist/types/types.d.ts +90 -0
  118. package/extract-react-types/modal-attributes.tsx +5 -0
  119. package/hooks/package.json +7 -0
  120. package/modal-body/package.json +7 -0
  121. package/modal-dialog/package.json +7 -0
  122. package/modal-footer/package.json +7 -0
  123. package/modal-header/package.json +7 -0
  124. package/modal-title/package.json +7 -0
  125. package/modal-transition/package.json +7 -0
  126. package/package.json +113 -0
  127. package/types/package.json +7 -0
package/LICENSE ADDED
@@ -0,0 +1,13 @@
1
+ Copyright 2019 Atlassian Pty Ltd
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
package/README.md ADDED
@@ -0,0 +1,13 @@
1
+ # Modal dialog
2
+
3
+ A modal dialog displays content that requires user interaction, in a layer above the page.
4
+
5
+ # Installation
6
+
7
+ ```sh
8
+ yarn add @atlaskit/modal-dialog
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ Detailed docs and example usage can be found [here](https://atlassian.design/components/modal-dialog/).
@@ -0,0 +1,42 @@
1
+ import React, { useState } from 'react';
2
+
3
+ import Button from '@atlaskit/button/standard-button';
4
+
5
+ import Modal, {
6
+ ModalBody,
7
+ ModalFooter,
8
+ ModalHeader,
9
+ ModalTitle,
10
+ ModalTransition,
11
+ } from '../src';
12
+
13
+ export default function Example() {
14
+ const [isOpen, setIsOpen] = useState(false);
15
+ const close = () => setIsOpen(false);
16
+ const open = () => setIsOpen(true);
17
+
18
+ return (
19
+ <>
20
+ <button onClick={open}>open modal</button>
21
+
22
+ <ModalTransition>
23
+ {isOpen && (
24
+ <Modal onClose={close}>
25
+ <ModalHeader>
26
+ <ModalTitle>Modal title</ModalTitle>
27
+ </ModalHeader>
28
+ <ModalBody>A simple Modal</ModalBody>
29
+ <ModalFooter>
30
+ <Button testId="primary" appearance="primary" onClick={close}>
31
+ Close
32
+ </Button>
33
+ <Button testId="secondary" appearance="subtle">
34
+ Secondary Action
35
+ </Button>
36
+ </ModalFooter>
37
+ </Modal>
38
+ )}
39
+ </ModalTransition>
40
+ </>
41
+ );
42
+ }
@@ -0,0 +1,136 @@
1
+ import React, { useCallback, useState } from 'react';
2
+
3
+ import { findByText, fireEvent } from '@testing-library/dom';
4
+ import {
5
+ InteractionTaskArgs,
6
+ PublicInteractionTask,
7
+ } from 'storybook-addon-performance';
8
+
9
+ import Button from '@atlaskit/button/standard-button';
10
+
11
+ import Modal, {
12
+ ModalBody,
13
+ ModalFooter,
14
+ ModalHeader,
15
+ ModalTitle,
16
+ ModalTransition,
17
+ } from '../src';
18
+
19
+ const modalText = (index: number) => `Hello, world (${index})`;
20
+ const closeText = (index: number) => `Close (${index})`;
21
+ const openText = (index: number) => `Open (${index})`;
22
+
23
+ const InteractionPerformance = ({ index = 0 }: { index?: number }) => {
24
+ const [isOpen, setIsOpen] = useState(false);
25
+ const open = useCallback(() => setIsOpen(true), []);
26
+ const close = useCallback(() => setIsOpen(false), []);
27
+
28
+ return (
29
+ <>
30
+ <button data-testid="open-button" onClick={open}>
31
+ {openText(index)}
32
+ </button>
33
+
34
+ <ModalTransition>
35
+ {isOpen && (
36
+ <Modal onClose={close}>
37
+ <ModalHeader>
38
+ <ModalTitle>Modal dialog</ModalTitle>
39
+ </ModalHeader>
40
+ <ModalBody>
41
+ {modalText(index)}
42
+ <InteractionPerformance index={index + 1} />
43
+ </ModalBody>
44
+ <ModalFooter>
45
+ <Button testId="primary" appearance="primary" onClick={close}>
46
+ {closeText(index)}
47
+ </Button>
48
+ </ModalFooter>
49
+ </Modal>
50
+ )}
51
+ </ModalTransition>
52
+ </>
53
+ );
54
+ };
55
+
56
+ const interactionTasks: PublicInteractionTask[] = [
57
+ {
58
+ name: 'Open first',
59
+ description: 'Opens the modal dialog',
60
+ run: async ({
61
+ container,
62
+ controls,
63
+ }: InteractionTaskArgs): Promise<void> => {
64
+ const openButton = await findByText(container, openText(0));
65
+
66
+ await controls.time(async () => {
67
+ fireEvent.click(openButton);
68
+ await findByText(document.body, modalText(0));
69
+ });
70
+ },
71
+ },
72
+ {
73
+ name: 'Close first',
74
+ description: 'Closes the modal dialog',
75
+ run: async ({
76
+ container,
77
+ controls,
78
+ }: InteractionTaskArgs): Promise<void> => {
79
+ const openButton = await findByText(container, openText(0));
80
+ fireEvent.click(openButton);
81
+
82
+ await controls.time(async () => {
83
+ const closeButton = await findByText(document.body, closeText(0));
84
+ fireEvent.click(closeButton);
85
+ });
86
+ },
87
+ },
88
+ {
89
+ name: 'Open second',
90
+ description: 'Opens the a second modal',
91
+ run: async ({
92
+ container,
93
+ controls,
94
+ }: InteractionTaskArgs): Promise<void> => {
95
+ const openButton = await findByText(container, openText(0));
96
+ fireEvent.click(openButton);
97
+ await findByText(document.body, modalText(0));
98
+ const secondOpenButton = await findByText(document.body, openText(1));
99
+
100
+ await controls.time(async () => {
101
+ fireEvent.click(secondOpenButton);
102
+ await findByText(document.body, modalText(1));
103
+ });
104
+ },
105
+ },
106
+ {
107
+ name: 'Close second',
108
+ description: 'Closes the a second modal',
109
+ run: async ({
110
+ container,
111
+ controls,
112
+ }: InteractionTaskArgs): Promise<void> => {
113
+ const openButton = await findByText(container, openText(0));
114
+ fireEvent.click(openButton);
115
+ await findByText(document.body, modalText(0));
116
+ const secondOpenButton = await findByText(document.body, openText(1));
117
+ fireEvent.click(secondOpenButton);
118
+ await findByText(document.body, modalText(1));
119
+
120
+ await controls.time(async () => {
121
+ const closeButton = await findByText(document.body, closeText(1));
122
+ fireEvent.click(closeButton);
123
+ });
124
+ },
125
+ },
126
+ ];
127
+
128
+ InteractionPerformance.story = {
129
+ parameters: {
130
+ performance: {
131
+ interactions: interactionTasks,
132
+ },
133
+ },
134
+ };
135
+
136
+ export default InteractionPerformance;
@@ -0,0 +1,98 @@
1
+ import React, { useCallback, useRef, useState } from 'react';
2
+
3
+ import { findByTestId, findByText, fireEvent } from '@testing-library/dom';
4
+ import Lorem from 'react-lorem-component';
5
+ import {
6
+ InteractionTaskArgs,
7
+ PublicInteractionTask,
8
+ } from 'storybook-addon-performance';
9
+
10
+ import Button from '@atlaskit/button/standard-button';
11
+
12
+ import Modal, {
13
+ ModalBody,
14
+ ModalFooter,
15
+ ModalHeader,
16
+ ModalTitle,
17
+ ModalTransition,
18
+ } from '../src';
19
+
20
+ const openText = 'Open modal';
21
+ const closeText = 'Close';
22
+ const scrollToBottomText = 'Scroll to bottom';
23
+
24
+ const ScrollPerformance = () => {
25
+ const [isOpen, setIsOpen] = useState(false);
26
+
27
+ const bottomRef = useRef<HTMLDivElement>(null);
28
+
29
+ const open = useCallback(() => setIsOpen(true), []);
30
+ const close = useCallback(() => setIsOpen(false), []);
31
+
32
+ const scrollToBottom = () =>
33
+ bottomRef.current && bottomRef.current.scrollIntoView(true);
34
+
35
+ return (
36
+ <>
37
+ <button data-testid="open" onClick={open}>
38
+ {openText}
39
+ </button>
40
+
41
+ <ModalTransition>
42
+ {isOpen && (
43
+ <Modal onClose={close} testId="modal">
44
+ <ModalHeader>
45
+ <ModalTitle>Modal dialog</ModalTitle>
46
+ </ModalHeader>
47
+ <ModalBody>
48
+ <Lorem count={10} />
49
+ <div ref={bottomRef} />
50
+ </ModalBody>
51
+ <ModalFooter>
52
+ <Button testId="close" appearance="primary" onClick={close}>
53
+ {closeText}
54
+ </Button>
55
+ <Button
56
+ testId="scroll-to-bottom"
57
+ appearance="subtle"
58
+ onClick={scrollToBottom}
59
+ >
60
+ {scrollToBottomText}
61
+ </Button>
62
+ </ModalFooter>
63
+ </Modal>
64
+ )}
65
+ </ModalTransition>
66
+ </>
67
+ );
68
+ };
69
+
70
+ const interactionTasks: PublicInteractionTask[] = [
71
+ {
72
+ name: 'Scroll to bottom',
73
+ description: 'Opens the modal dialog',
74
+ run: async ({
75
+ container,
76
+ controls,
77
+ }: InteractionTaskArgs): Promise<void> => {
78
+ const openButton = await findByText(container, openText);
79
+ fireEvent.click(openButton);
80
+
81
+ const content = await findByTestId(document.body, 'modal--scrollable');
82
+
83
+ await controls.time(async () => {
84
+ content.scrollTo(0, content.scrollHeight);
85
+ });
86
+ },
87
+ },
88
+ ];
89
+
90
+ ScrollPerformance.story = {
91
+ parameters: {
92
+ performance: {
93
+ interactions: interactionTasks,
94
+ },
95
+ },
96
+ };
97
+
98
+ export default ScrollPerformance;
@@ -0,0 +1,51 @@
1
+ import { JSCodeshift } from 'jscodeshift';
2
+ import { Collection } from 'jscodeshift/src/Collection';
3
+
4
+ import {
5
+ createTransformer,
6
+ hasImportDeclaration,
7
+ } from '@atlaskit/codemod-utils';
8
+
9
+ import { handlePropSpread } from './migrations/handle-prop-spread';
10
+ import { inlineWidthNamesDeclaration } from './migrations/inline-WidthNames-declaration';
11
+ import { mapActionsProp } from './migrations/map-actions-prop';
12
+ import { mapBodyFromProps } from './migrations/map-body-from-props';
13
+ import { mapContainerFromProps } from './migrations/map-container-from-props';
14
+ import { mapFooterFromProps } from './migrations/map-footer-from-props';
15
+ import { mapHeaderFromProps } from './migrations/map-header-from-props';
16
+ import { mapHeadingPropToModalTitle } from './migrations/map-heading-prop';
17
+ import { removeAppearanceProp } from './migrations/remove-appearance-prop';
18
+ import { removeComponentOverrideProps } from './migrations/remove-component-override-props';
19
+ import { removeIsChromeless } from './migrations/remove-is-chromeless';
20
+ import { renameAppearanceType } from './migrations/rename-appearance-type';
21
+ import { renameInnerComponentPropTypes } from './migrations/rename-inner-component-prop-types';
22
+ import { renameScrollBehaviorToShouldScrollInViewport } from './migrations/rename-scrollBehavior-to-shouldScrollInViewport';
23
+
24
+ /**
25
+ * The order of these migrations matters!
26
+ * Mapping the container usage, along with removal of key props
27
+ * such as the 'components' and 'appearance' should come last
28
+ * after the other migrations.
29
+ */
30
+ const transformer = createTransformer(
31
+ [
32
+ mapBodyFromProps,
33
+ mapHeaderFromProps,
34
+ mapFooterFromProps,
35
+ renameScrollBehaviorToShouldScrollInViewport,
36
+ renameAppearanceType,
37
+ renameInnerComponentPropTypes,
38
+ inlineWidthNamesDeclaration,
39
+ mapHeadingPropToModalTitle,
40
+ mapActionsProp,
41
+ mapContainerFromProps,
42
+ removeComponentOverrideProps,
43
+ removeAppearanceProp,
44
+ removeIsChromeless,
45
+ handlePropSpread,
46
+ ],
47
+ (j: JSCodeshift, source: Collection<Node>) =>
48
+ hasImportDeclaration(j, source, '@atlaskit/modal-dialog'),
49
+ );
50
+
51
+ export default transformer;
@@ -0,0 +1,493 @@
1
+ import transformer from '../12.0.0-lite-mode';
2
+
3
+ const defineInlineTest = require('jscodeshift/dist/testUtils').defineInlineTest;
4
+
5
+ describe('12.0.0-lite-mode', () => {
6
+ ['tsx', 'babylon'].forEach((parser) => {
7
+ describe(`parser: ${parser}`, () => {
8
+ defineInlineTest(
9
+ { default: transformer, parser },
10
+ {},
11
+ `
12
+ import React, { useState } from 'react';
13
+
14
+ import ModalDialog from '@atlaskit/modal-dialog';
15
+
16
+ const Container = (props) => (<form>{props.children}</form>);
17
+ const Header = (props) => (<div><h1>{props.children}</h1></div>);
18
+ const Footer = (props) => (<div>{props.children}</div>);
19
+
20
+ export default function modalDialog() {
21
+ const [appearance, setAppearance] = useState(null);
22
+
23
+ return (
24
+ <ModalDialog
25
+ appearance={appearance}
26
+ scrollBehavior="outside"
27
+ onClose={noop}
28
+ components={{ Container }}
29
+ header={Header}
30
+ footer={Footer}>
31
+ <p>Content #1</p>
32
+ <p>Content #2</p>
33
+ </ModalDialog>
34
+ );
35
+ }
36
+ `,
37
+ `
38
+ /* TODO: (from codemod)\u0020
39
+ We have converted this file as best we could but you might still need
40
+ to manually complete migrating this usage of ModalDialog.
41
+
42
+ This file uses one or more of the following ModalDialog props: 'components', 'header',
43
+ 'footer', 'body'. These props have been removed as part of moving to
44
+ a compositional API.
45
+
46
+ The render props that used to be exposed by the custom component APIs are
47
+ now accessible using the 'useModal' hook instead: 'testId', 'titleId', and 'onClose'.
48
+
49
+ We are also no longer exposing 'appearance' as render prop, so this needs to be
50
+ manually passed to your custom components.
51
+
52
+ If you are using the 'container' value of 'components' to wrap ModalDialog in something
53
+ other than a 'form', you'll need to add the style 'all: inherit;' for scrolling to function.
54
+
55
+ For a complete guide on customization using the new compositional API, refer to the docs at
56
+ https://atlassian.design/components/modal-dialog/examples. */
57
+ import React, { useState } from 'react';
58
+
59
+ import ModalDialog, { ModalBody } from "@atlaskit/modal-dialog";
60
+
61
+ const Container = (props) => (<form>{props.children}</form>);
62
+ const Header = (props) => (<div><h1>{props.children}</h1></div>);
63
+ const Footer = (props) => (<div>{props.children}</div>);
64
+
65
+ export default function modalDialog() {
66
+ const [appearance, setAppearance] = useState(null);
67
+
68
+ return (
69
+ <ModalDialog shouldScrollInViewport onClose={noop}>
70
+ {Container({
71
+ children: <>
72
+ {Header({
73
+ appearance: appearance
74
+ })}
75
+ <ModalBody>
76
+ <p>Content #1</p>
77
+ <p>Content #2</p>
78
+ </ModalBody>
79
+ {Footer({
80
+ appearance: appearance
81
+ })}
82
+ </>
83
+ })}
84
+ </ModalDialog>
85
+ );
86
+ }
87
+ `,
88
+ 'should change custom usages in the appropriate order',
89
+ );
90
+
91
+ defineInlineTest(
92
+ { default: transformer, parser },
93
+ {},
94
+ `
95
+ import React, { useState } from 'react';
96
+
97
+ import ModalDialog, {
98
+ AppearanceType,
99
+ HeaderComponentProps,
100
+ FooterComponentProps,
101
+ BodyComponentProps
102
+ } from '@atlaskit/modal-dialog';
103
+
104
+ const Container = (props) => (<form>{props.children}</form>);
105
+ const Header = (props: HeaderComponentProps) => (<div><h1>{props.children}</h1></div>);
106
+ const Footer = (props: FooterComponentProps) => (<div>{props.children}</div>);
107
+
108
+ export default function modalDialog() {
109
+ const [appearance, setAppearance] = useState<AppearanceType | null>(null);
110
+
111
+ return (
112
+ <ModalDialog
113
+ appearance={appearance}
114
+ scrollBehavior="outside"
115
+ onClose={noop}
116
+ components={{ Container }}
117
+ header={Header}
118
+ footer={Footer} />
119
+ );
120
+ }
121
+ `,
122
+ `
123
+ /* TODO: (from codemod)\u0020
124
+ We have converted this file as best we could but you might still need
125
+ to manually complete migrating this usage of ModalDialog.
126
+
127
+ This file uses one or more of the following ModalDialog props: 'components', 'header',
128
+ 'footer', 'body'. These props have been removed as part of moving to
129
+ a compositional API.
130
+
131
+ The render props that used to be exposed by the custom component APIs are
132
+ now accessible using the 'useModal' hook instead: 'testId', 'titleId', and 'onClose'.
133
+
134
+ We are also no longer exposing 'appearance' as render prop, so this needs to be
135
+ manually passed to your custom components.
136
+
137
+ If you are using the 'container' value of 'components' to wrap ModalDialog in something
138
+ other than a 'form', you'll need to add the style 'all: inherit;' for scrolling to function.
139
+
140
+ For a complete guide on customization using the new compositional API, refer to the docs at
141
+ https://atlassian.design/components/modal-dialog/examples. */
142
+ import React, { useState } from 'react';
143
+
144
+ import ModalDialog, {
145
+ Appearance as AppearanceType,
146
+ ModalHeaderProps as HeaderComponentProps,
147
+ ModalFooterProps as FooterComponentProps,
148
+ ModalBodyProps as BodyComponentProps
149
+ } from '@atlaskit/modal-dialog';
150
+
151
+ const Container = (props) => (<form>{props.children}</form>);
152
+ const Header = (props: HeaderComponentProps) => (<div><h1>{props.children}</h1></div>);
153
+ const Footer = (props: FooterComponentProps) => (<div>{props.children}</div>);
154
+
155
+ export default function modalDialog() {
156
+ const [appearance, setAppearance] = useState<AppearanceType | null>(null);
157
+
158
+ return (
159
+ <ModalDialog shouldScrollInViewport onClose={noop}>
160
+ {Container({
161
+ children: <>
162
+ {Header({
163
+ appearance: appearance
164
+ })}{Footer({
165
+ appearance: appearance
166
+ })}
167
+ </>
168
+ })}
169
+ </ModalDialog>
170
+ );
171
+ }
172
+ `,
173
+ 'should change custom usages for a self-closing modal without body',
174
+ );
175
+
176
+ defineInlineTest(
177
+ { default: transformer, parser },
178
+ {},
179
+ `
180
+ import React, { useState } from 'react';
181
+
182
+ import ModalDialog, { ModalTransition } from '@atlaskit/modal-dialog';
183
+
184
+ export default function modalDialog(props) {
185
+ const [isOpen, toggleOpen] = useState<boolean>(false);
186
+ const { appearance, multiline, onClose } = props;
187
+
188
+ const actions = [
189
+ { text: 'Close', onClick: close, testId: 'primary' },
190
+ {
191
+ text: 'Secondary Action',
192
+ onClick: noop,
193
+ testId: 'secondary',
194
+ },
195
+ ];
196
+
197
+ return (
198
+ <ModalTransition>
199
+ {isOpen &&
200
+ <ModalDialog
201
+ appearance={appearance}
202
+ actions={actions}
203
+ onClose={onClose}
204
+ heading="Modal dialog"
205
+ isHeadingMultiline={multiline}
206
+ isChromeless={false}
207
+ scrollBehavior="outside"
208
+ {...props}
209
+ >
210
+ <div>Content #1</div>
211
+ <div>Content #2</div>
212
+ </ModalDialog>
213
+ }
214
+ </ModalTransition>
215
+ );
216
+ }
217
+ `,
218
+ `
219
+ /* TODO: (from codemod)\u0020
220
+ This file is spreading props on the ModalDialog component, so we could not
221
+ automatically convert this usage to the new API.
222
+
223
+ The following props have been deprecated as part of moving to a compositional API:
224
+
225
+ - 'heading' prop has been replaced by ModalHeader and ModalTitle components.
226
+ - 'actions' prop has been replaced by ModalFooter component, with Button components from @atlaskit/button.
227
+ - 'scrollBehavior' prop has been replaced by 'shouldScrollInViewport', where "outside" from the previous prop maps to true in the new prop.
228
+ - 'isHeadingMultiline' prop has been replaced by 'isMultiline' prop on the ModalTitle component.
229
+ - 'appearance' prop has been moved to the ModalTitle component. To achieve the feature parity, pass the 'appearance' prop directly to ModalTitle and Button components inside ModalFooter.
230
+
231
+ Refer to the docs for the new API at https://atlassian.design/components/modal-dialog/examples
232
+ to complete the migration and use the new composable components. */
233
+ import React, { useState } from 'react';
234
+
235
+ import Button from "@atlaskit/button/standard-button";
236
+ import ModalDialog, { ModalTransition, ModalBody, ModalTitle, ModalHeader, ModalFooter } from "@atlaskit/modal-dialog";
237
+
238
+ export default function modalDialog(props) {
239
+ const [isOpen, toggleOpen] = useState<boolean>(false);
240
+ const { appearance, multiline, onClose } = props;
241
+
242
+ const actions = [
243
+ { text: 'Close', onClick: close, testId: 'primary' },
244
+ {
245
+ text: 'Secondary Action',
246
+ onClick: noop,
247
+ testId: 'secondary',
248
+ },
249
+ ];
250
+
251
+ return (
252
+ <ModalTransition>
253
+ {isOpen &&
254
+ <ModalDialog onClose={onClose} shouldScrollInViewport {...props}>
255
+ <ModalHeader>
256
+ <ModalTitle appearance={appearance} isMultiline={multiline}>
257
+ Modal dialog
258
+ </ModalTitle>
259
+ </ModalHeader>
260
+
261
+ <ModalBody>
262
+ <div>Content #1</div>
263
+ <div>Content #2</div>
264
+ </ModalBody>
265
+ <ModalFooter>
266
+ {actions.map((props, index) => <Button
267
+ {...props}
268
+ autoFocus={index === 0}
269
+ appearance={index === 0 ? appearance || "primary" : "subtle"}>{props.text}</Button>).reverse()}
270
+ </ModalFooter>
271
+ </ModalDialog>
272
+ }
273
+ </ModalTransition>
274
+ );
275
+ }
276
+ `,
277
+ 'should change default usages for modal dialog',
278
+ );
279
+
280
+ defineInlineTest(
281
+ { default: transformer, parser },
282
+ {},
283
+ `
284
+ import React, { useState } from 'react';
285
+
286
+ import ModalDialog, { ModalTransition, ModalBody } from '@atlaskit/modal-dialog';
287
+
288
+ import { getModalDialogActions } from './utils';
289
+
290
+ const Container = (props) => (<form>{props.children}</form>);
291
+ const CustomBody = (props) => (<div><ModalBody>{props.children}</ModalBody></div>);
292
+
293
+ export default function modalDialog(props) {
294
+ const [isOpen, toggleOpen] = useState<boolean>(false);
295
+ const { appearance, onClose } = props;
296
+
297
+ return (
298
+ <ModalTransition>
299
+ {isOpen &&
300
+ <ModalDialog
301
+ components={{ Container, Body: CustomBody }}
302
+ actions={getModalDialogActions(onClose)}
303
+ onClose={onClose}
304
+ heading="Modal dialog"
305
+ isChromeless={true}
306
+ scrollBehavior="outside"
307
+ >
308
+ <div>Content #1</div>
309
+ <div>Content #2</div>
310
+ </ModalDialog>
311
+ }
312
+ </ModalTransition>
313
+ );
314
+ }
315
+ `,
316
+ `
317
+ /* TODO: (from codemod)\u0020
318
+ We have converted this file as best we could but you might still need
319
+ to manually complete migrating this usage of ModalDialog.
320
+
321
+ This file uses one or more of the following ModalDialog props: 'components', 'header',
322
+ 'footer', 'body'. These props have been removed as part of moving to
323
+ a compositional API.
324
+
325
+ The render props that used to be exposed by the custom component APIs are
326
+ now accessible using the 'useModal' hook instead: 'testId', 'titleId', and 'onClose'.
327
+
328
+ We are also no longer exposing 'appearance' as render prop, so this needs to be
329
+ manually passed to your custom components.
330
+
331
+ If you are using the 'container' value of 'components' to wrap ModalDialog in something
332
+ other than a 'form', you'll need to add the style 'all: inherit;' for scrolling to function.
333
+
334
+ For a complete guide on customization using the new compositional API, refer to the docs at
335
+ https://atlassian.design/components/modal-dialog/examples. */
336
+ import React, { useState } from 'react';
337
+
338
+ import Button from "@atlaskit/button/standard-button";
339
+ import ModalDialog, { ModalTransition, ModalBody, ModalTitle, ModalHeader, ModalFooter } from "@atlaskit/modal-dialog";
340
+
341
+ import { getModalDialogActions } from './utils';
342
+
343
+ const Container = (props) => (<form>{props.children}</form>);
344
+ const CustomBody = (props) => (<div><ModalBody>{props.children}</ModalBody></div>);
345
+
346
+ export default function modalDialog(props) {
347
+ const [isOpen, toggleOpen] = useState<boolean>(false);
348
+ const { appearance, onClose } = props;
349
+
350
+ return (
351
+ <ModalTransition>
352
+ {isOpen &&
353
+ /* TODO: (from codemod)\u0020
354
+ ModalDialog has a new compositional API and the 'isChromeless' prop is no longer supported.
355
+ To have the functionality of the 'isChromeless' prop, you can choose to not use any of the default exports (ModalBody, ModalHeader and ModalFooter).
356
+ The only other change is that ModalDialog's children should have a border radius of 3px to match the box shadow.
357
+ For more information, check the documentation at https://atlassian.design/components/modal-dialog/examples */
358
+ <ModalDialog onClose={onClose} shouldScrollInViewport>
359
+ {Container({
360
+ children: <>
361
+ <ModalHeader>
362
+ <ModalTitle>
363
+ Modal dialog
364
+ </ModalTitle>
365
+ </ModalHeader>
366
+
367
+ {CustomBody({
368
+ children: <>
369
+ <div>Content #1</div>
370
+ <div>Content #2</div>
371
+ </>
372
+ })}
373
+ <ModalFooter>
374
+ {getModalDialogActions(onClose).map((props, index) => <Button
375
+ {...props}
376
+ appearance={index === 0 ? props.appearance || "primary" : props.appearance || "subtle"}>{props.text}</Button>)}
377
+ </ModalFooter>
378
+ </>
379
+ })}
380
+ </ModalDialog>
381
+ }
382
+ </ModalTransition>
383
+ );
384
+ }
385
+ `,
386
+ 'should change mixed usages (custom + default) for modal dialog',
387
+ );
388
+ });
389
+ });
390
+
391
+ // Only support ts for this case
392
+ defineInlineTest(
393
+ { default: transformer, parser: 'tsx' },
394
+ {},
395
+ `
396
+ import React, { useState } from 'react';
397
+
398
+ import ModalDialog, {
399
+ AppearanceType,
400
+ HeaderComponentProps,
401
+ FooterComponentProps,
402
+ } from '@atlaskit/modal-dialog';
403
+
404
+ import { WidthNames } from '@atlaskit/modal-dialog/shared-variables';
405
+
406
+ interface CustomHeaderProps extends HeaderComponentProps {
407
+ size?: WidthNames;
408
+ }
409
+
410
+ const Container = (props) => (<form>{props.children}</form>);
411
+ const Header = (props: CustomHeaderProps) => (<div><h1>{props.children}</h1></div>);
412
+ const Footer = (props: FooterComponentProps) => (<div>{props.children}</div>);
413
+
414
+ export default function modalDialog() {
415
+ const [appearance, setAppearance] = useState<AppearanceType | null>(null);
416
+
417
+ return (
418
+ <ModalDialog
419
+ appearance={appearance}
420
+ scrollBehavior="outside"
421
+ onClose={noop}
422
+ components={{ Container }}
423
+ header={Header}
424
+ footer={Footer}>
425
+ <p>Content #1</p>
426
+ <p>Content #2</p>
427
+ </ModalDialog>
428
+ );
429
+ }
430
+ `,
431
+ `
432
+ /* TODO: (from codemod)\u0020
433
+ We have converted this file as best we could but you might still need
434
+ to manually complete migrating this usage of ModalDialog.
435
+
436
+ This file uses one or more of the following ModalDialog props: 'components', 'header',
437
+ 'footer', 'body'. These props have been removed as part of moving to
438
+ a compositional API.
439
+
440
+ The render props that used to be exposed by the custom component APIs are
441
+ now accessible using the 'useModal' hook instead: 'testId', 'titleId', and 'onClose'.
442
+
443
+ We are also no longer exposing 'appearance' as render prop, so this needs to be
444
+ manually passed to your custom components.
445
+
446
+ If you are using the 'container' value of 'components' to wrap ModalDialog in something
447
+ other than a 'form', you'll need to add the style 'all: inherit;' for scrolling to function.
448
+
449
+ For a complete guide on customization using the new compositional API, refer to the docs at
450
+ https://atlassian.design/components/modal-dialog/examples. */
451
+ import React, { useState } from 'react';
452
+
453
+ import ModalDialog, {
454
+ Appearance as AppearanceType,
455
+ ModalHeaderProps as HeaderComponentProps,
456
+ ModalFooterProps as FooterComponentProps,
457
+ ModalBody,
458
+ } from "@atlaskit/modal-dialog";
459
+
460
+ interface CustomHeaderProps extends HeaderComponentProps {
461
+ size?: "small" | "medium" | "large" | "x-large";
462
+ }
463
+
464
+ const Container = (props) => (<form>{props.children}</form>);
465
+ const Header = (props: CustomHeaderProps) => (<div><h1>{props.children}</h1></div>);
466
+ const Footer = (props: FooterComponentProps) => (<div>{props.children}</div>);
467
+
468
+ export default function modalDialog() {
469
+ const [appearance, setAppearance] = useState<AppearanceType | null>(null);
470
+
471
+ return (
472
+ <ModalDialog shouldScrollInViewport onClose={noop}>
473
+ {Container({
474
+ children: <>
475
+ {Header({
476
+ appearance: appearance
477
+ })}
478
+ <ModalBody>
479
+ <p>Content #1</p>
480
+ <p>Content #2</p>
481
+ </ModalBody>
482
+ {Footer({
483
+ appearance: appearance
484
+ })}
485
+ </>
486
+ })}
487
+ </ModalDialog>
488
+ );
489
+ }
490
+ `,
491
+ 'should change custom usages and types in the appropriate order',
492
+ );
493
+ });