From d1ca50aa9d6b4fef4fde34acc9a95211270be2e9 Mon Sep 17 00:00:00 2001 From: Ollie Curtis <8831547+olliecurtis@users.noreply.github.com> Date: Thu, 18 Jul 2024 09:12:22 +0100 Subject: [PATCH] [ARGG-1158][BpkDialog]: Migrate Dialog to not use Portal (#3508) [ARGG-1158][BpkDialog]: Migrate Dialog to not use Portal (#3508) * [ARGG-1158]: Migrate Dialog to not use Portal * Add floating portal to allow rendering outside of target and update tests * Update example to render inside pagewrap * Increase story timeout --- examples/bpk-component-dialog/examples.js | 186 ------- examples/bpk-component-dialog/examples.tsx | 163 ++++++ examples/bpk-component-dialog/stories.js | 46 -- examples/bpk-component-dialog/stories.tsx | 97 ++++ .../src/BpkDialog-test.tsx | 290 ++++++----- .../bpk-component-dialog/src/BpkDialog.tsx | 71 ++- .../src/__snapshots__/BpkDialog-test.tsx.snap | 472 ------------------ .../src/accessibility-test.tsx | 31 +- 8 files changed, 480 insertions(+), 876 deletions(-) delete mode 100644 examples/bpk-component-dialog/examples.js create mode 100644 examples/bpk-component-dialog/examples.tsx delete mode 100644 examples/bpk-component-dialog/stories.js create mode 100644 examples/bpk-component-dialog/stories.tsx delete mode 100644 packages/bpk-component-dialog/src/__snapshots__/BpkDialog-test.tsx.snap diff --git a/examples/bpk-component-dialog/examples.js b/examples/bpk-component-dialog/examples.js deleted file mode 100644 index cdb51470b5..0000000000 --- a/examples/bpk-component-dialog/examples.js +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Backpack - Skyscanner's Design System - * - * Copyright 2016 Skyscanner Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* @flow strict */ - -import PropTypes from 'prop-types'; -import { Component } from 'react'; -import type { Node } from 'react'; - -import BpkButton from '../../packages/bpk-component-button'; -import BpkDialog, { - HEADER_ICON_TYPES, -} from '../../packages/bpk-component-dialog'; -import InfoIcon from '../../packages/bpk-component-icon/lg/information-circle'; -import TickIcon from '../../packages/bpk-component-icon/lg/tick'; -import TrashIcon from '../../packages/bpk-component-icon/lg/trash'; -import BpkText, { TEXT_STYLES } from '../../packages/bpk-component-text'; -import { cssModules, withDefaultProps } from '../../packages/bpk-react-utils'; - -import STYLES from './examples.module.scss'; - -const getClassName = cssModules(STYLES); - -const Paragraph = withDefaultProps(BpkText, { - textStyle: TEXT_STYLES.bodyDefault, - tagName: 'p', - className: getClassName('bpk-dialog-paragraph'), -}); - -type Props = { - children: Node, - dismissible: boolean, - headerIcon: ?Node, - initiallyOpen: ?boolean, -}; - -type State = { - isOpen: boolean, -}; - -class DialogContainer extends Component { - static propTypes = { - children: PropTypes.node.isRequired, - dismissible: PropTypes.bool, - }; - - static defaultProps = { - dismissible: true, - headerIcon: null, - }; - - constructor(props: Props) { - super(); - - this.state = { - isOpen: props.initiallyOpen || false, - }; - } - - onOpen = () => { - this.setState({ - isOpen: true, - }); - }; - - onClose = () => { - this.setState({ - isOpen: false, - }); - }; - - render() { - return ( -
-
- Open dialog -
- {/* $FlowFixMe[cannot-spread-inexact] - inexact rest. See 'decisions/flowfixme.md'. */} - document.getElementById('pagewrap')} - renderTarget={() => document.getElementById('dialog-container')} - headerIcon - {...this.props} - > - {this.props.children} - Close - -
- ); - } -} - -const DefaultExample = () => ( - - - This is a default dialog. You can put anything you want in here. - - -); - -const WithIconExample = () => ( -
-
- Default Icon Dialog - } dismissible={false}> - - This is a default dialog with an icon. You can put anything you want - in here. - - -
-
-
- Warning Icon Dialog - } - headerIconType={HEADER_ICON_TYPES.warning} - dismissible={false} - > - - This is a warning dialog with an icon. You can put anything you want - in here. - - -
-
-
- Destructive Icon Dialog - } - headerIconType={HEADER_ICON_TYPES.destructive} - dismissible={false} - > - - This is a destructive dialog with an icon. You can put anything you - want in here. - - -
-
-); - -const NotDismissibleExample = () => ( - - - This is not dismissible. To close it you must bind the `onClose` function - to a component inside the dialog, like the button below. - - -); - -const WithFlareExample = () => ( - - - This is a dialog with a flare view added as the header. - - -); - -export { - DefaultExample, - WithIconExample, - NotDismissibleExample, - WithFlareExample, -}; diff --git a/examples/bpk-component-dialog/examples.tsx b/examples/bpk-component-dialog/examples.tsx new file mode 100644 index 0000000000..a770d36667 --- /dev/null +++ b/examples/bpk-component-dialog/examples.tsx @@ -0,0 +1,163 @@ +/* + * Backpack - Skyscanner's Design System + * + * Copyright 2016 Skyscanner Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { useState } from 'react'; +import type { ReactElement, ReactNode } from 'react'; + +import BpkButton from '../../packages/bpk-component-button'; +import BpkDialog, { + HEADER_ICON_TYPES, +} from '../../packages/bpk-component-dialog'; +import InfoIcon from '../../packages/bpk-component-icon/lg/information-circle'; +import TickIcon from '../../packages/bpk-component-icon/lg/tick'; +import TrashIcon from '../../packages/bpk-component-icon/lg/trash'; +import BpkText, { TEXT_STYLES } from '../../packages/bpk-component-text'; +import { cssModules, withDefaultProps } from '../../packages/bpk-react-utils'; + +import STYLES from './examples.module.scss'; + +const getClassName = cssModules(STYLES); + +const Paragraph = withDefaultProps(BpkText, { + textStyle: TEXT_STYLES.bodyDefault, + tagName: 'p', + className: getClassName('bpk-dialog-paragraph'), +}); + +type Props = { + children: ReactElement; + dismissible?: boolean; + initallyOpen?: boolean; + headerIcon?: ReactNode; + headerIconType?: (typeof HEADER_ICON_TYPES)[keyof typeof HEADER_ICON_TYPES]; + flare?: boolean; +}; + +const DialogContainer = (props: Props) => { + const { children, dismissible = true, initallyOpen = false, ...rest } = props; + const [isOpenState, setIsOpen] = useState(initallyOpen); + const onOpen = () => { + setIsOpen(true); + }; + + const onClose = () => { + setIsOpen(false); + }; + + return ( +
+
+ Open dialog +
+ document.getElementById('pagewrap')} + renderTarget={() => document.getElementById('dialog-container')} + dismissible={dismissible} + {...rest} + > + {children} + Close + +
+ ); +}; + +const DefaultExample = () => ( + + + This is a default dialog. You can put anything you want in here. + + +); + +const WithPrimaryIconExample = () => ( +
+ Primary Icon Dialog + } initallyOpen dismissible={false}> + + This is a default dialog with an icon. You can put anything you want in + here. + + +
+); + +const WithWarningIconExample = () => ( +
+ Warning Icon Dialog + } + headerIconType={HEADER_ICON_TYPES.warning} + initallyOpen + dismissible={false} + > + + This is a warning dialog with an icon. You can put anything you want in + here. + + +
+); + +const WithDestructiveIconExample = () => ( +
+ Destructive Icon Dialog + } + headerIconType={HEADER_ICON_TYPES.destructive} + initallyOpen + dismissible={false} + > + + This is a destructive dialog with an icon. You can put anything you want + in here. + + +
+); + +const NotDismissibleExample = () => ( + + + This is not dismissible. To close it you must bind the `onClose` function + to a component inside the dialog, like the button below. + + +); + +const WithFlareExample = () => ( + + + This is a dialog with a flare view added as the header. + + +); + +export { + DefaultExample, + WithPrimaryIconExample, + WithWarningIconExample, + WithDestructiveIconExample, + NotDismissibleExample, + WithFlareExample, +}; diff --git a/examples/bpk-component-dialog/stories.js b/examples/bpk-component-dialog/stories.js deleted file mode 100644 index ac0291a2c1..0000000000 --- a/examples/bpk-component-dialog/stories.js +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Backpack - Skyscanner's Design System - * - * Copyright 2016 Skyscanner Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -import BpkDialog from '../../packages/bpk-component-dialog/src/BpkDialog'; - -import { - DefaultExample, - WithIconExample, - NotDismissibleExample, - WithFlareExample, -} from './examples'; - -export default { - title: 'bpk-component-dialog', - component: BpkDialog, -}; - -export const Default = DefaultExample; -export const WithAnIcon = WithIconExample; - -export const NotDismissible = NotDismissibleExample; - -export const WithFlare = WithFlareExample; - -export const VisualTest = DefaultExample; - -export const VisualTestWithZoom = VisualTest.bind({}); -VisualTestWithZoom.args = { - zoomEnabled: true -}; diff --git a/examples/bpk-component-dialog/stories.tsx b/examples/bpk-component-dialog/stories.tsx new file mode 100644 index 0000000000..b9c81964e5 --- /dev/null +++ b/examples/bpk-component-dialog/stories.tsx @@ -0,0 +1,97 @@ +/* + * Backpack - Skyscanner's Design System + * + * Copyright 2016 Skyscanner Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +import BpkDialog from '../../packages/bpk-component-dialog/src/BpkDialog'; + +import { + DefaultExample, + WithPrimaryIconExample, + WithWarningIconExample, + WithDestructiveIconExample, + NotDismissibleExample, + WithFlareExample, +} from './examples'; + +export default { + title: 'bpk-component-dialog', + component: BpkDialog, +}; + +export const Default = DefaultExample; +export const WithAPrimaryIcon = WithPrimaryIconExample; +export const WithAWarningIcon = WithWarningIconExample; +export const WithADestructiveIcon = WithDestructiveIconExample; +export const NotDismissible = NotDismissibleExample; +export const WithFlare = WithFlareExample; + +// Due to how iframes work we can pass a local url to load the stories above. +// Attempted to use a Custom Iframe component with a react portal and ref to +// render components but it didn't have the desired effect. +const VisualWrapper = ({id, zoomEnabled = false}: {id: string, zoomEnabled?: boolean}) => ( +
+