aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web/design-system/src/components/modal
diff options
context:
space:
mode:
authorDavid Cho-Lerat <david.cho-lerat@sonarsource.com>2023-05-12 16:25:44 +0200
committersonartech <sonartech@sonarsource.com>2023-05-17 20:02:41 +0000
commit8e10ac9ab50a1e2a0f40ba1ad07907a8017a02df (patch)
treeb49cef0a9647a88dcef14a8adb55cca7ca0c84f4 /server/sonar-web/design-system/src/components/modal
parenta4e7896a64696dab6beacc0219534060a755ad20 (diff)
downloadsonarqube-8e10ac9ab50a1e2a0f40ba1ad07907a8017a02df.tar.gz
sonarqube-8e10ac9ab50a1e2a0f40ba1ad07907a8017a02df.zip
SONAR-19170 Create new Modal component in design-system/
Diffstat (limited to 'server/sonar-web/design-system/src/components/modal')
-rw-r--r--server/sonar-web/design-system/src/components/modal/Modal.tsx158
-rw-r--r--server/sonar-web/design-system/src/components/modal/ModalBody.tsx47
-rw-r--r--server/sonar-web/design-system/src/components/modal/ModalFooter.tsx43
-rw-r--r--server/sonar-web/design-system/src/components/modal/ModalHeader.tsx51
-rw-r--r--server/sonar-web/design-system/src/components/modal/__tests__/Modal-test.tsx90
-rw-r--r--server/sonar-web/design-system/src/components/modal/__tests__/ModalBody-test.tsx33
-rw-r--r--server/sonar-web/design-system/src/components/modal/__tests__/ModalFooter-test.tsx56
-rw-r--r--server/sonar-web/design-system/src/components/modal/__tests__/ModalHeader-test.tsx45
-rw-r--r--server/sonar-web/design-system/src/components/modal/__tests__/__snapshots__/ModalBody-test.tsx.snap29
-rw-r--r--server/sonar-web/design-system/src/components/modal/__tests__/__snapshots__/ModalFooter-test.tsx.snap66
-rw-r--r--server/sonar-web/design-system/src/components/modal/__tests__/__snapshots__/ModalHeader-test.tsx.snap75
11 files changed, 693 insertions, 0 deletions
diff --git a/server/sonar-web/design-system/src/components/modal/Modal.tsx b/server/sonar-web/design-system/src/components/modal/Modal.tsx
new file mode 100644
index 00000000000..95d6213cbb8
--- /dev/null
+++ b/server/sonar-web/design-system/src/components/modal/Modal.tsx
@@ -0,0 +1,158 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+import { Global, css, useTheme } from '@emotion/react';
+import classNames from 'classnames';
+import { ReactNode } from 'react';
+import ReactModal from 'react-modal';
+import tw from 'twin.macro';
+import { themeColor } from '../../helpers';
+import { REACT_DOM_CONTAINER } from '../../helpers/constants';
+import { translate } from '../../helpers/l10n';
+import { Theme } from '../../types/theme';
+import { ButtonSecondary } from '../buttons';
+import { ModalBody } from './ModalBody';
+import { ModalFooter } from './ModalFooter';
+import { ModalHeader } from './ModalHeader';
+
+ReactModal.setAppElement(REACT_DOM_CONTAINER);
+
+interface CommonProps {
+ closeOnOverlayClick?: boolean;
+ isLarge?: boolean;
+ isOpen?: boolean;
+ isScrollable?: boolean;
+ onClose: VoidFunction;
+}
+
+interface ChildrenProp {
+ children: React.ReactNode;
+}
+
+interface NotChildrenProp {
+ children?: never;
+}
+
+interface SectionsProps {
+ body: React.ReactNode;
+ headerDescription?: string | ReactNode;
+ headerTitle: string | ReactNode;
+ loading?: boolean;
+ primaryButton?: ReactNode;
+ secondaryButtonLabel: ReactNode;
+}
+
+type NotSectionsProps = {
+ [prop in keyof SectionsProps]?: never;
+};
+
+export type PropsWithChildren = CommonProps & ChildrenProp & NotSectionsProps;
+
+export type PropsWithSections = CommonProps & SectionsProps & NotChildrenProp;
+
+type Props = PropsWithChildren | PropsWithSections;
+
+function hasNoChildren(props: Partial<Props>): props is PropsWithSections {
+ return (props as PropsWithChildren).children === undefined;
+}
+
+export function Modal({
+ closeOnOverlayClick = true,
+ isLarge,
+ isOpen = true,
+ isScrollable = true,
+ onClose,
+ ...props
+}: Props) {
+ const theme = useTheme();
+
+ return (
+ <>
+ <Global styles={globalStyles({ theme })} />
+
+ <ReactModal
+ className={classNames('design-system-modal-contents', { large: isLarge })}
+ isOpen={isOpen}
+ onRequestClose={onClose}
+ overlayClassName="design-system-modal-overlay"
+ shouldCloseOnEsc={true}
+ shouldCloseOnOverlayClick={closeOnOverlayClick}
+ shouldFocusAfterRender={true}
+ shouldReturnFocusAfterClose={true}
+ >
+ {hasNoChildren(props) ? (
+ <>
+ <ModalHeader description={props.headerDescription} title={props.headerTitle} />
+
+ <ModalBody isScrollable={isScrollable}>{props.body}</ModalBody>
+
+ <ModalFooter
+ loading={props.loading}
+ primaryButton={props.primaryButton}
+ secondaryButton={
+ <ButtonSecondary
+ className="js-modal-close sw-capitalize"
+ disabled={props.loading}
+ onClick={onClose}
+ type="reset"
+ >
+ {props.secondaryButtonLabel ?? translate('close')}
+ </ButtonSecondary>
+ }
+ />
+ </>
+ ) : (
+ (props as PropsWithChildren).children
+ )}
+ </ReactModal>
+ </>
+ );
+}
+
+const globalStyles = ({ theme }: { theme: Theme }) => css`
+ .design-system-modal-contents {
+ ${tw`sw-container sw-flex sw-flex-col`}
+ ${tw`sw-p-9`}
+ ${tw`sw-rounded-2`}
+ ${tw`sw-z-modal`}
+
+ background-color: ${themeColor('modalContents')({ theme })};
+ max-height: calc(100vh - 30px);
+ min-height: 160px;
+ width: 544px;
+
+ &.large {
+ max-width: 1280px;
+ min-width: 1040px;
+ }
+ }
+
+ .design-system-modal-overlay {
+ ${tw`sw-fixed sw-inset-0`}
+ ${tw`sw-flex sw-items-center sw-justify-center`}
+ ${tw`sw-z-modal-overlay`}
+
+ background-color: ${themeColor('modalOverlay')({ theme })};
+ }
+`;
+
+Modal.Body = ModalBody;
+Modal.Footer = ModalFooter;
+Modal.Header = ModalHeader;
diff --git a/server/sonar-web/design-system/src/components/modal/ModalBody.tsx b/server/sonar-web/design-system/src/components/modal/ModalBody.tsx
new file mode 100644
index 00000000000..5a3b1f19832
--- /dev/null
+++ b/server/sonar-web/design-system/src/components/modal/ModalBody.tsx
@@ -0,0 +1,47 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+import styled from '@emotion/styled';
+import classNames from 'classnames';
+import { ReactNode } from 'react';
+import tw from 'twin.macro';
+import { themeColor } from '../../helpers/theme';
+
+interface Props {
+ children: ReactNode;
+ isScrollable?: boolean;
+}
+
+export function ModalBody({ children, isScrollable = true }: Props) {
+ return <StyledMain className={classNames({ scrollable: isScrollable })}>{children}</StyledMain>;
+}
+
+const StyledMain = styled.main`
+ ${tw`sw-body-sm`}
+ ${tw`sw-pr-3`} // to accomodate a possible scrollbar
+ ${tw`sw-my-12`}
+ ${tw`sw-overflow-x-hidden`}
+
+ color: ${themeColor('pageContent')};
+
+ &.scrollable {
+ overflow-y: auto;
+ }
+`;
diff --git a/server/sonar-web/design-system/src/components/modal/ModalFooter.tsx b/server/sonar-web/design-system/src/components/modal/ModalFooter.tsx
new file mode 100644
index 00000000000..72d75b6cce5
--- /dev/null
+++ b/server/sonar-web/design-system/src/components/modal/ModalFooter.tsx
@@ -0,0 +1,43 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+import styled from '@emotion/styled';
+import tw from 'twin.macro';
+import { DeferredSpinner } from '../DeferredSpinner';
+
+interface Props {
+ loading?: boolean;
+ primaryButton?: React.ReactNode;
+ secondaryButton: React.ReactNode;
+}
+
+export function ModalFooter({ loading = false, primaryButton, secondaryButton }: Props) {
+ return (
+ <StyledFooter>
+ <DeferredSpinner loading={loading} />
+ {primaryButton}
+ {secondaryButton}
+ </StyledFooter>
+ );
+}
+
+const StyledFooter = styled.footer`
+ ${tw`sw-flex sw-gap-3 sw-items-center sw-justify-end`}
+`;
diff --git a/server/sonar-web/design-system/src/components/modal/ModalHeader.tsx b/server/sonar-web/design-system/src/components/modal/ModalHeader.tsx
new file mode 100644
index 00000000000..eaa02e175cb
--- /dev/null
+++ b/server/sonar-web/design-system/src/components/modal/ModalHeader.tsx
@@ -0,0 +1,51 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+import styled from '@emotion/styled';
+import { ReactNode } from 'react';
+import tw from 'twin.macro';
+import { themeColor } from '../../helpers/theme';
+
+interface Props {
+ description?: string | ReactNode;
+ title: string | ReactNode;
+}
+
+export function ModalHeader({ description, title }: Props) {
+ return (
+ <header>
+ <Title>{title}</Title>
+ {description && <Description>{description}</Description>}
+ </header>
+ );
+}
+
+const Description = styled.p`
+ ${tw`sw-body-sm`}
+ ${tw`sw-mt-2`}
+
+ color: ${themeColor('pageContent')};
+`;
+
+const Title = styled.p`
+ ${tw`sw-heading-lg`}
+
+ color: ${themeColor('pageTitle')};
+`;
diff --git a/server/sonar-web/design-system/src/components/modal/__tests__/Modal-test.tsx b/server/sonar-web/design-system/src/components/modal/__tests__/Modal-test.tsx
new file mode 100644
index 00000000000..86085f547dd
--- /dev/null
+++ b/server/sonar-web/design-system/src/components/modal/__tests__/Modal-test.tsx
@@ -0,0 +1,90 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+import { screen } from '@testing-library/react';
+import { render } from '../../../helpers/testUtils';
+import { Modal, PropsWithChildren, PropsWithSections } from '../Modal';
+
+it('should render default modal with predefined content', async () => {
+ setupPredefinedContent({
+ body: 'Modal body',
+ headerTitle: 'Hello',
+ headerDescription: 'Does this look OK?',
+ secondaryButtonLabel: undefined, // should use the default of 'Close'
+ });
+
+ expect(await screen.findByText('Modal body')).toBeVisible();
+ expect(await screen.findByText('Hello')).toBeVisible();
+ expect(await screen.findByText('Does this look OK?')).toBeVisible();
+ expect(await screen.findByRole('button', { name: 'close' })).toBeVisible();
+});
+
+it('should request close when pressing esc', async () => {
+ const onClose = jest.fn();
+ const { user } = setupPredefinedContent({ onClose });
+
+ await user.keyboard('{Escape}');
+
+ expect(onClose).toHaveBeenCalled();
+});
+
+it('should render modal with loose content', async () => {
+ setupLooseContent(undefined, <div>Hello</div>);
+
+ expect(await screen.findByText('Hello')).toBeVisible();
+});
+
+it('should request close when pressing esc on loose content', async () => {
+ const onClose = jest.fn();
+ const { user } = setupLooseContentWithMultipleChildren({ onClose });
+
+ await user.keyboard('{Escape}');
+
+ expect(onClose).toHaveBeenCalled();
+});
+
+function setupPredefinedContent(props: Partial<PropsWithSections> = {}) {
+ return render(
+ <Modal
+ body="Body"
+ headerTitle="Hello"
+ onClose={jest.fn()}
+ secondaryButtonLabel="Close"
+ {...props}
+ />
+ );
+}
+
+function setupLooseContent(props: Partial<PropsWithChildren> = {}, children = <div />) {
+ return render(
+ <Modal onClose={jest.fn()} {...props}>
+ {children}
+ </Modal>
+ );
+}
+
+function setupLooseContentWithMultipleChildren(props: Partial<PropsWithChildren> = {}) {
+ return render(
+ <Modal onClose={jest.fn()} {...props}>
+ <div>Hello there!</div>
+ <div>How are you?</div>
+ </Modal>
+ );
+}
diff --git a/server/sonar-web/design-system/src/components/modal/__tests__/ModalBody-test.tsx b/server/sonar-web/design-system/src/components/modal/__tests__/ModalBody-test.tsx
new file mode 100644
index 00000000000..690cd99d821
--- /dev/null
+++ b/server/sonar-web/design-system/src/components/modal/__tests__/ModalBody-test.tsx
@@ -0,0 +1,33 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+import { render } from '../../../helpers/testUtils';
+import { ModalBody } from '../ModalBody';
+
+it('renders with children', () => {
+ const children = <div>Hello!</div>;
+ const { container } = setup(children);
+
+ expect(container).toMatchSnapshot();
+});
+
+function setup(children = <div />) {
+ return render(<ModalBody>{children}</ModalBody>);
+}
diff --git a/server/sonar-web/design-system/src/components/modal/__tests__/ModalFooter-test.tsx b/server/sonar-web/design-system/src/components/modal/__tests__/ModalFooter-test.tsx
new file mode 100644
index 00000000000..ab2c19c133d
--- /dev/null
+++ b/server/sonar-web/design-system/src/components/modal/__tests__/ModalFooter-test.tsx
@@ -0,0 +1,56 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+import { render } from '../../../helpers/testUtils';
+import { FCProps } from '../../../types/misc';
+import { ModalFooter } from '../ModalFooter';
+
+it('should render with secondary button', () => {
+ const { container } = setupWithProps({
+ secondaryButton: (
+ <button onClick={() => {}} type="button">
+ Close
+ </button>
+ ),
+ });
+
+ expect(container).toMatchSnapshot();
+});
+
+it('should render with primary and secondary buttons', () => {
+ const { container } = setupWithProps({
+ primaryButton: (
+ <button onClick={undefined} type="button">
+ Primary
+ </button>
+ ),
+ secondaryButton: (
+ <button onClick={undefined} type="reset">
+ Reset
+ </button>
+ ),
+ });
+
+ expect(container).toMatchSnapshot();
+});
+
+function setupWithProps(props: Partial<FCProps<typeof ModalFooter>> = {}) {
+ return render(<ModalFooter secondaryButton={<div />} {...props} />);
+}
diff --git a/server/sonar-web/design-system/src/components/modal/__tests__/ModalHeader-test.tsx b/server/sonar-web/design-system/src/components/modal/__tests__/ModalHeader-test.tsx
new file mode 100644
index 00000000000..a3a163a66d1
--- /dev/null
+++ b/server/sonar-web/design-system/src/components/modal/__tests__/ModalHeader-test.tsx
@@ -0,0 +1,45 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+import { render } from '../../../helpers/testUtils';
+import { FCProps } from '../../../types/misc';
+import { ModalHeader } from '../ModalHeader';
+
+it('should use the default title if not provided', () => {
+ const { container } = setupWithProps();
+
+ expect(container).toMatchSnapshot();
+});
+
+it('should render with title', () => {
+ const { container } = setupWithProps({ title: 'Foo' });
+
+ expect(container).toMatchSnapshot();
+});
+
+it('should render with title and description', () => {
+ const { container } = setupWithProps({ title: 'Foo', description: 'Bar' });
+
+ expect(container).toMatchSnapshot();
+});
+
+function setupWithProps(props: Partial<FCProps<typeof ModalHeader>> = {}) {
+ return render(<ModalHeader title="Modal title" {...props} />);
+}
diff --git a/server/sonar-web/design-system/src/components/modal/__tests__/__snapshots__/ModalBody-test.tsx.snap b/server/sonar-web/design-system/src/components/modal/__tests__/__snapshots__/ModalBody-test.tsx.snap
new file mode 100644
index 00000000000..8f64b28df6b
--- /dev/null
+++ b/server/sonar-web/design-system/src/components/modal/__tests__/__snapshots__/ModalBody-test.tsx.snap
@@ -0,0 +1,29 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders with children 1`] = `
+.emotion-0 {
+ font-family: Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";
+ font-size: 0.875rem;
+ line-height: 1.25rem;
+ font-weight: 400;
+ padding-right: 0.75rem;
+ margin-top: 3rem;
+ margin-bottom: 3rem;
+ overflow-x: hidden;
+ color: rgb(62,67,87);
+}
+
+.emotion-0.scrollable {
+ overflow-y: auto;
+}
+
+<div>
+ <main
+ class="scrollable emotion-0 emotion-1"
+ >
+ <div>
+ Hello!
+ </div>
+ </main>
+</div>
+`;
diff --git a/server/sonar-web/design-system/src/components/modal/__tests__/__snapshots__/ModalFooter-test.tsx.snap b/server/sonar-web/design-system/src/components/modal/__tests__/__snapshots__/ModalFooter-test.tsx.snap
new file mode 100644
index 00000000000..250bd886c0e
--- /dev/null
+++ b/server/sonar-web/design-system/src/components/modal/__tests__/__snapshots__/ModalFooter-test.tsx.snap
@@ -0,0 +1,66 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render with primary and secondary buttons 1`] = `
+.emotion-0 {
+ display: -webkit-box;
+ display: -webkit-flex;
+ display: -ms-flexbox;
+ display: flex;
+ -webkit-align-items: center;
+ -webkit-box-align: center;
+ -ms-flex-align: center;
+ align-items: center;
+ -webkit-box-pack: end;
+ -ms-flex-pack: end;
+ -webkit-justify-content: flex-end;
+ justify-content: flex-end;
+ gap: 0.75rem;
+}
+
+<div>
+ <footer
+ class="emotion-0 emotion-1"
+ >
+ <button
+ type="button"
+ >
+ Primary
+ </button>
+ <button
+ type="reset"
+ >
+ Reset
+ </button>
+ </footer>
+</div>
+`;
+
+exports[`should render with secondary button 1`] = `
+.emotion-0 {
+ display: -webkit-box;
+ display: -webkit-flex;
+ display: -ms-flexbox;
+ display: flex;
+ -webkit-align-items: center;
+ -webkit-box-align: center;
+ -ms-flex-align: center;
+ align-items: center;
+ -webkit-box-pack: end;
+ -ms-flex-pack: end;
+ -webkit-justify-content: flex-end;
+ justify-content: flex-end;
+ gap: 0.75rem;
+}
+
+<div>
+ <footer
+ class="emotion-0 emotion-1"
+ >
+ <button
+ type="button"
+ >
+ Close
+ </button>
+ </footer>
+</div>
+`;
diff --git a/server/sonar-web/design-system/src/components/modal/__tests__/__snapshots__/ModalHeader-test.tsx.snap b/server/sonar-web/design-system/src/components/modal/__tests__/__snapshots__/ModalHeader-test.tsx.snap
new file mode 100644
index 00000000000..10d1204b2e5
--- /dev/null
+++ b/server/sonar-web/design-system/src/components/modal/__tests__/__snapshots__/ModalHeader-test.tsx.snap
@@ -0,0 +1,75 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render with title 1`] = `
+.emotion-0 {
+ font-family: Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";
+ font-size: 1.5rem;
+ line-height: 1.75rem;
+ font-weight: 600;
+ color: rgb(29,33,47);
+}
+
+<div>
+ <header>
+ <p
+ class="emotion-0 emotion-1"
+ >
+ Foo
+ </p>
+ </header>
+</div>
+`;
+
+exports[`should render with title and description 1`] = `
+.emotion-0 {
+ font-family: Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";
+ font-size: 1.5rem;
+ line-height: 1.75rem;
+ font-weight: 600;
+ color: rgb(29,33,47);
+}
+
+.emotion-2 {
+ font-family: Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";
+ font-size: 0.875rem;
+ line-height: 1.25rem;
+ font-weight: 400;
+ margin-top: 0.5rem;
+ color: rgb(62,67,87);
+}
+
+<div>
+ <header>
+ <p
+ class="emotion-0 emotion-1"
+ >
+ Foo
+ </p>
+ <p
+ class="emotion-2 emotion-3"
+ >
+ Bar
+ </p>
+ </header>
+</div>
+`;
+
+exports[`should use the default title if not provided 1`] = `
+.emotion-0 {
+ font-family: Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";
+ font-size: 1.5rem;
+ line-height: 1.75rem;
+ font-weight: 600;
+ color: rgb(29,33,47);
+}
+
+<div>
+ <header>
+ <p
+ class="emotion-0 emotion-1"
+ >
+ Modal title
+ </p>
+ </header>
+</div>
+`;