From 017ef2fd43dab2cf839109807ba427b1c7918876 Mon Sep 17 00:00:00 2001 From: Quentin Gliech Date: Thu, 15 Feb 2024 20:08:44 +0100 Subject: [PATCH] Revamp the confirmation dialog --- frontend/locales/en.json | 4 +- frontend/package-lock.json | 20 ++- frontend/package.json | 2 + .../ConfirmationModal.module.css | 42 ----- .../ConfirmationModal.test.tsx | 147 +++++------------- .../ConfirmationModal/ConfirmationModal.tsx | 73 +++------ .../ConfirmationModal.test.tsx.snap | 81 ++++++---- .../src/components/Dialog/Dialog.module.css | 136 ++++++++++++++++ .../src/components/Dialog/Dialog.stories.tsx | 56 +++++++ frontend/src/components/Dialog/Dialog.tsx | 113 ++++++++++++++ frontend/src/components/Dialog/index.ts | 15 ++ .../components/Session/EndSessionButton.tsx | 4 - .../components/UserEmail/UserEmail.module.css | 18 +++ .../src/components/UserEmail/UserEmail.tsx | 48 ++++-- 14 files changed, 507 insertions(+), 252 deletions(-) delete mode 100644 frontend/src/components/ConfirmationModal/ConfirmationModal.module.css create mode 100644 frontend/src/components/Dialog/Dialog.module.css create mode 100644 frontend/src/components/Dialog/Dialog.stories.tsx create mode 100644 frontend/src/components/Dialog/Dialog.tsx create mode 100644 frontend/src/components/Dialog/index.ts diff --git a/frontend/locales/en.json b/frontend/locales/en.json index 91b0d2887..a52489515 100644 --- a/frontend/locales/en.json +++ b/frontend/locales/en.json @@ -2,6 +2,7 @@ "action": { "back": "Back", "cancel": "Cancel", + "close": "Close", "continue": "Continue", "save": "Save" }, @@ -146,7 +147,8 @@ "user_email": { "cant_delete_primary": "Choose a different primary email to delete this one.", "delete_button_confirmation_modal": { - "body": "Are you sure you want to remove this email?" + "action": "Delete email", + "body": "Delete this email?" }, "delete_button_title": "Remove email address", "email": "Email", diff --git a/frontend/package-lock.json b/frontend/package-lock.json index ffa2171e7..a5fd66037 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -11,6 +11,7 @@ "@fontsource/inconsolata": "^5.0.16", "@fontsource/inter": "^5.0.16", "@radix-ui/react-alert-dialog": "^1.0.5", + "@radix-ui/react-dialog": "^1.0.5", "@radix-ui/react-form": "^0.0.3", "@tanstack/react-router": "^1.16.2", "@types/ua-parser-js": "^0.7.39", @@ -33,6 +34,7 @@ "react-i18next": "^14.0.5", "ua-parser-js": "^1.0.37", "urql": "^4.0.6", + "vaul": "^0.9.0", "zod": "^3.22.4" }, "devDependencies": { @@ -9360,6 +9362,18 @@ } } }, + "node_modules/@vector-im/compound-web/node_modules/vaul": { + "version": "0.7.9", + "resolved": "https://registry.npmjs.org/vaul/-/vaul-0.7.9.tgz", + "integrity": "sha512-RrcnGOHOq/cEU3YpyyZrnjh0H79xMpF3IrHZs9ichvHlpKjLDc4Vwjn4VkuGzeUGrmQ3wamfm/cpdKWpvBIgQw==", + "dependencies": { + "@radix-ui/react-dialog": "^1.0.4" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + } + }, "node_modules/@vitejs/plugin-react": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.2.1.tgz", @@ -23240,9 +23254,9 @@ } }, "node_modules/vaul": { - "version": "0.7.9", - "resolved": "https://registry.npmjs.org/vaul/-/vaul-0.7.9.tgz", - "integrity": "sha512-RrcnGOHOq/cEU3YpyyZrnjh0H79xMpF3IrHZs9ichvHlpKjLDc4Vwjn4VkuGzeUGrmQ3wamfm/cpdKWpvBIgQw==", + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/vaul/-/vaul-0.9.0.tgz", + "integrity": "sha512-bZSySGbAHiTXmZychprnX/dE0EsSige88xtyyL3/MCRbrFotRPQZo7UdydGXZWw+CKbNOw5Ow8gwAo93/nB/Cg==", "dependencies": { "@radix-ui/react-dialog": "^1.0.4" }, diff --git a/frontend/package.json b/frontend/package.json index 069b67632..725d0ab34 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -19,6 +19,7 @@ "@fontsource/inconsolata": "^5.0.16", "@fontsource/inter": "^5.0.16", "@radix-ui/react-alert-dialog": "^1.0.5", + "@radix-ui/react-dialog": "^1.0.5", "@radix-ui/react-form": "^0.0.3", "@tanstack/react-router": "^1.16.2", "@types/ua-parser-js": "^0.7.39", @@ -41,6 +42,7 @@ "react-i18next": "^14.0.5", "ua-parser-js": "^1.0.37", "urql": "^4.0.6", + "vaul": "^0.9.0", "zod": "^3.22.4" }, "devDependencies": { diff --git a/frontend/src/components/ConfirmationModal/ConfirmationModal.module.css b/frontend/src/components/ConfirmationModal/ConfirmationModal.module.css deleted file mode 100644 index b181dec6e..000000000 --- a/frontend/src/components/ConfirmationModal/ConfirmationModal.module.css +++ /dev/null @@ -1,42 +0,0 @@ -/* Copyright 2023 The Matrix.org Foundation C.I.C. - * - * 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. - */ - -.buttons { - display: flex; - flex-direction: row; - justify-content: flex-end; - gap: var(--cpd-space-2x); -} - -.overlay { - position: fixed; - inset: 0; - background-color: #666; - opacity: 0.4; -} - -.content { - position: fixed; - margin: auto; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - /* less than the magic 378 content width set in Layout */ - width: 300px; - padding: var(--cpd-space-4x); - border-radius: var(--cpd-space-2x); - background-color: var(--cpd-color-bg-canvas-default); - border: 1px solid var(--cpd-color-border-interactive-primary); -} \ No newline at end of file diff --git a/frontend/src/components/ConfirmationModal/ConfirmationModal.test.tsx b/frontend/src/components/ConfirmationModal/ConfirmationModal.test.tsx index 391f3f5fd..5f496609a 100644 --- a/frontend/src/components/ConfirmationModal/ConfirmationModal.test.tsx +++ b/frontend/src/components/ConfirmationModal/ConfirmationModal.test.tsx @@ -15,6 +15,7 @@ // @vitest-environment happy-dom import { render, cleanup, screen, fireEvent } from "@testing-library/react"; +import { TooltipProvider } from "@vector-im/compound-web"; import { describe, expect, it, afterEach, vi } from "vitest"; import ConfirmationModal from "./ConfirmationModal"; @@ -26,90 +27,55 @@ describe("", () => { it("does not render a closed modal", () => { const onConfirm = vi.fn(); - const onDeny = vi.fn(); render( - - Some extra information. - , + + + Some extra information. + + , ); expect(screen.getByText("Open modal")).toBeTruthy(); - expect(screen.queryByRole("alertdialog")).toBeFalsy(); + expect(screen.queryByRole("dialog")).toBeFalsy(); }); it("opens modal on clicking trigger", () => { const onConfirm = vi.fn(); - const onDeny = vi.fn(); render( - - Some extra information. - , + + + Some extra information. + + , ); fireEvent.click(screen.getByText("Open modal")); - expect(screen.getByRole("alertdialog")).toMatchSnapshot(); - }); - - it("renders an undeniable modal", () => { - const onConfirm = vi.fn(); - const onDeny = undefined; - - render( - - Some extra information. - , - ); - - fireEvent.click(screen.getByText("Open modal")); - - // no cancel button without onDeny - expect(screen.queryByText("Cancel")).toBeFalsy(); - - // The dialog does not close on escape - fireEvent.keyDown(screen.getByRole("alertdialog"), { - key: "Escape", - code: "Escape", - keyCode: 27, - }); - - // dialog still open - expect(screen.queryByRole("alertdialog")).toBeTruthy(); + expect(screen.getByRole("dialog")).toMatchSnapshot(); }); it("calls onConfirm on confirmation", () => { const onConfirm = vi.fn(); - const onDeny = vi.fn(); render( - - Some extra information. - , + + + Some extra information. + + , ); fireEvent.click(screen.getByText("Open modal")); @@ -118,60 +84,33 @@ describe("", () => { expect(onConfirm).toHaveBeenCalled(); // dialog closed - expect(screen.queryByRole("alertdialog")).toBeFalsy(); + expect(screen.queryByRole("dialog")).toBeFalsy(); }); - it("calls onDeny on cancel click", () => { + it("closes modal with Esc", () => { const onConfirm = vi.fn(); - const onDeny = vi.fn(); render( - - Some extra information. - , + + + Some extra information. + + , ); fireEvent.click(screen.getByText("Open modal")); - fireEvent.click(screen.getByText("Cancel")); - expect(onDeny).toHaveBeenCalled(); - - // dialog closed - expect(screen.queryByRole("alertdialog")).toBeFalsy(); - }); - - it("calls onDeny on closing modal via Esc", () => { - const onConfirm = vi.fn(); - const onDeny = vi.fn(); - - render( - - Some extra information. - , - ); - - fireEvent.click(screen.getByText("Open modal")); - - fireEvent.keyDown(screen.getByRole("alertdialog"), { + fireEvent.keyDown(screen.getByRole("dialog"), { key: "Escape", code: "Escape", keyCode: 27, }); - expect(onDeny).toHaveBeenCalled(); // dialog closed - expect(screen.queryByRole("alertdialog")).toBeFalsy(); + expect(screen.queryByRole("dialog")).toBeFalsy(); }); }); diff --git a/frontend/src/components/ConfirmationModal/ConfirmationModal.tsx b/frontend/src/components/ConfirmationModal/ConfirmationModal.tsx index 8b01a510b..ae597f345 100644 --- a/frontend/src/components/ConfirmationModal/ConfirmationModal.tsx +++ b/frontend/src/components/ConfirmationModal/ConfirmationModal.tsx @@ -1,4 +1,4 @@ -// Copyright 2023 The Matrix.org Foundation C.I.C. +// Copyright 2023, 2024 The Matrix.org Foundation C.I.C. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -12,82 +12,49 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { - Root, - Portal, - Overlay, - Content, - Trigger, - Title, - Description, - Action, - Cancel, -} from "@radix-ui/react-alert-dialog"; import { Button } from "@vector-im/compound-web"; -import classNames from "classnames"; import { ReactNode } from "react"; import { Translation } from "react-i18next"; -import styles from "./ConfirmationModal.module.css"; +import { Dialog, Title, Description, Close, Actions } from "../Dialog"; type Props = { onConfirm: () => void; - onDeny?: () => void; title?: ReactNode | string; // element used to trigger opening of modal trigger: ReactNode; - className?: string; }; + /** * Generic confirmation modal * controls its own open state - * calls onDeny on cancel or esc * calls onConfirm on confirm click */ const ConfirmationModal: React.FC> = ({ onConfirm, - onDeny, - className, children, trigger, title, }) => ( {(t): ReactNode => ( - - {trigger} - - - { - if (onDeny) { - onDeny(); - } else { - // if there is no deny callback, we should prevent the escape key from closing the modal - event.preventDefault(); - } - }} - > - {title} - {children} -
- {onDeny && ( - - - - )} - - - -
-
-
-
+ + {title} + + {children && {children}} + + + + + + + + + + + )}
); diff --git a/frontend/src/components/ConfirmationModal/__snapshots__/ConfirmationModal.test.tsx.snap b/frontend/src/components/ConfirmationModal/__snapshots__/ConfirmationModal.test.tsx.snap index 114f09f1d..4d4db1dd4 100644 --- a/frontend/src/components/ConfirmationModal/__snapshots__/ConfirmationModal.test.tsx.snap +++ b/frontend/src/components/ConfirmationModal/__snapshots__/ConfirmationModal.test.tsx.snap @@ -4,45 +4,68 @@ exports[` > opens modal on clicking trigger 1`] = `
-

- Are you sure? -

-

- Some extra information. -

+

+ Are you sure? +

+

+ Some extra information. +

+
+ + +
-
diff --git a/frontend/src/components/Dialog/Dialog.module.css b/frontend/src/components/Dialog/Dialog.module.css new file mode 100644 index 000000000..6770cb54f --- /dev/null +++ b/frontend/src/components/Dialog/Dialog.module.css @@ -0,0 +1,136 @@ +/* Copyright 2024 The Matrix.org Foundation C.I.C. + * + * 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. + */ + + +.overlay { + position: fixed; + inset: 0; + background: rgba(3 12 27 / 52.8%); +} + +.dialog { + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: calc(496px + var(--cpd-space-3x) * 2); + max-width: calc(100vw - var(--cpd-space-4x)); +} + +.body { + display: flex; + flex-direction: column; + gap: var(--cpd-space-8x); + background: var(--cpd-color-bg-canvas-default); +} + +.title { + font: var(--cpd-font-heading-md-semibold); + letter-spacing: var(--cpd-font-letter-spacing-heading-md); + color: var(--cpd-color-text-primary); + margin-block-start: var(--cpd-space-4x); +} + +.actions { + display: flex; + flex-direction: column; + gap: var(--cpd-space-4x); +} + +.dialog .body { + padding: var(--cpd-space-4x) var(--cpd-space-10x) var(--cpd-space-10x); +} + +.dialog .title { + padding-inline-end: var(--cpd-space-7x); +} + +.dialog .close { + position: absolute; + inset-block-start: var(--cpd-space-7x); + inset-inline-end: var(--cpd-space-7x); + padding: var(--cpd-space-1x); + border-radius: var(--cpd-radius-pill-effect); + background: var(--cpd-color-bg-subtle-secondary); + color: var(--cpd-color-icon-secondary); + + &>svg { + inline-size: var(--cpd-space-5x); + block-size: var(--cpd-space-5x); + } + + &:hover { + background: var(--cpd-color-bg-subtle-primary); + color: var(--cpd-color-icon-primary); + } +} + +.drawer { + position: fixed; + background: var(--cpd-color-bg-canvas-default); + inset-block-end: 0; + inset-inline: 0; + + /* Cap the inline content size at 520px, filling the rest of the space with + padding */ + padding-inline: max(0px, calc((100% - 520px) / 2)); + border-start-start-radius: var(--border-radius); + border-start-end-radius: var(--border-radius); + display: flex; + flex-direction: column; + + /* Drawer comes in the Android style by default */ + --border-radius: 28px; + --handle-block-size: 4px; + --handle-inline-size: 32px; + --handle-inset-block-start: var(--cpd-space-4x); + --handle-inset-block-end: 0px; + --content-inset-block-start: calc(var(--handle-inset-block-start) + var(--handle-block-size) + var(--handle-inset-block-end)); +} + +.drawer[data-platform="ios"] { + --border-radius: 10px; + --handle-block-size: 5px; + --handle-inline-size: 36px; + --handle-inset-block-start: var(--cpd-space-1-5x); + --handle-inset-block-end: 1px; +} + +.drawer .body { + padding-inline: var(--cpd-space-4x); + padding-block-start: calc(var(--content-inset-block-start) + var(--cpd-space-6x)); + padding-block-end: var(--cpd-space-12x); + border-start-start-radius: var(--border-radius); + border-start-end-radius: var(--border-radius); + + /* Even with overflow: auto, the content can still overflow at the corners + where it meets with the curved border. A contain: paint fixes that. */ + contain: paint; + overflow: auto; + scrollbar-width: none; + + --cpd-separator-spacing: 0; + --cpd-separator-inset: var(--cpd-space-4x); +} + +.drawer .body::before { + content: ""; + position: absolute; + block-size: var(--handle-block-size); + inset-inline: calc((100% - var(--handle-inline-size)) / 2); + inset-block-start: var(--handle-inset-block-start); + background: var(--cpd-color-icon-secondary); + border-radius: var(--cpd-radius-pill-effect); +} diff --git a/frontend/src/components/Dialog/Dialog.stories.tsx b/frontend/src/components/Dialog/Dialog.stories.tsx new file mode 100644 index 000000000..b4f32961a --- /dev/null +++ b/frontend/src/components/Dialog/Dialog.stories.tsx @@ -0,0 +1,56 @@ +// Copyright 2024 The Matrix.org Foundation C.I.C. +// +// 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 { action } from "@storybook/addon-actions"; +import type { Meta, StoryObj } from "@storybook/react"; + +import { Dialog, Title, Description } from "./Dialog"; + +const Template: React.FC<{ + title: string; + description: string; + asDrawer: boolean; + open: boolean; + onOpenChange: (open: boolean) => void; +}> = ({ title, description, asDrawer, open, onOpenChange }) => ( + + {title && {title}} + {description} + +); + +const meta = { + title: "UI/Dialog", + component: Template, + tags: ["autodocs"], + args: { + open: true, + title: "Title", + description: "Description", + asDrawer: false, + onOpenChange: action("onOpenChange"), + }, + argTypes: { + open: { control: "boolean" }, + title: { control: "text" }, + description: { control: "text" }, + asDrawer: { control: "boolean" }, + onOpenChange: { action: "onOpenChange" }, + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Basic: Story = {}; diff --git a/frontend/src/components/Dialog/Dialog.tsx b/frontend/src/components/Dialog/Dialog.tsx new file mode 100644 index 000000000..6d118c659 --- /dev/null +++ b/frontend/src/components/Dialog/Dialog.tsx @@ -0,0 +1,113 @@ +// Copyright 2024 The Matrix.org Foundation C.I.C. +// +// 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 { + Root as DialogRoot, + Trigger, + Portal, + Close, + Title as DialogTitle, + Overlay as DialogOverlay, + Content as DialogContent, +} from "@radix-ui/react-dialog"; +import IconClose from "@vector-im/compound-design-tokens/icons/close.svg?react"; +import { Glass, Tooltip } from "@vector-im/compound-web"; +import { PropsWithChildren } from "react"; +import { useTranslation } from "react-i18next"; +import { Drawer } from "vaul"; + +import styles from "./Dialog.module.css"; + +// The granularity of this value is kind of arbitrary: it distinguishes exactly +// the platforms that this library needs to know about in order to correctly +// implement the designs. +let platform: "android" | "ios" | "other" = "other"; + +if (/android/i.test(navigator.userAgent)) { + platform = "android"; + // We include 'Mac' here and double-check for touch support because iPads on + // iOS 13 pretend to be a MacOS desktop +} else if ( + /iPad|iPhone|iPod|Mac/.test(navigator.userAgent) && + "ontouchend" in document +) { + platform = "ios"; +} + +type Props = React.PropsWithChildren<{ + trigger?: React.ReactNode; + open?: boolean; + asDrawer?: boolean; + onOpenChange?: (open: boolean) => void; +}>; + +export const Dialog: React.FC = ({ + trigger, + open, + asDrawer, + onOpenChange, + children, +}) => { + if (typeof asDrawer !== "boolean") { + asDrawer = platform !== "other"; + } + + const { t } = useTranslation(); + + if (asDrawer) { + return ( + + {trigger && {trigger}} + + + +
{children}
+
+
+
+ ); + } + + return ( + + {trigger && {trigger}} + + + + +
+ {children} + + + + + + +
+
+
+
+
+ ); +}; + +export const Title: React.FC = ({ children }) => ( + {children} +); + +export const Actions: React.FC = ({ children }) => ( +
{children}
+); + +export { Description, Close } from "@radix-ui/react-dialog"; diff --git a/frontend/src/components/Dialog/index.ts b/frontend/src/components/Dialog/index.ts new file mode 100644 index 000000000..176e5490a --- /dev/null +++ b/frontend/src/components/Dialog/index.ts @@ -0,0 +1,15 @@ +// Copyright 2024 The Matrix.org Foundation C.I.C. +// +// 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. + +export { Close, Dialog, Title, Description, Actions } from "./Dialog"; diff --git a/frontend/src/components/Session/EndSessionButton.tsx b/frontend/src/components/Session/EndSessionButton.tsx index 7ce024309..03a38103f 100644 --- a/frontend/src/components/Session/EndSessionButton.tsx +++ b/frontend/src/components/Session/EndSessionButton.tsx @@ -39,13 +39,9 @@ const EndSessionButton: React.FC<{ endSession: () => Promise }> = ({ setInProgress(false); }; - // NOOP so we render cancel button - const onDeny = (): void => {}; - return ( <> svg { + color: var(--cpd-color-icon-secondary); + background-color: var(--cpd-color-bg-subtle-secondary); + padding: var(--cpd-space-2x); + border-radius: var(--cpd-space-2x); + inline-size: var(--cpd-space-10x); + block-size: var(--cpd-space-10x); + } +} + .link { display: inline-block; text-decoration: underline; diff --git a/frontend/src/components/UserEmail/UserEmail.tsx b/frontend/src/components/UserEmail/UserEmail.tsx index 0a5c5ff6e..56aa6d721 100644 --- a/frontend/src/components/UserEmail/UserEmail.tsx +++ b/frontend/src/components/UserEmail/UserEmail.tsx @@ -13,13 +13,14 @@ // limitations under the License. import IconDelete from "@vector-im/compound-design-tokens/icons/delete.svg?react"; -import { Form, IconButton, Text, Tooltip } from "@vector-im/compound-web"; +import IconEmail from "@vector-im/compound-design-tokens/icons/email.svg?react"; +import { Button, Form, IconButton, Tooltip } from "@vector-im/compound-web"; import { ComponentProps, ReactNode } from "react"; import { Translation, useTranslation } from "react-i18next"; import { useMutation } from "urql"; import { FragmentType, graphql, useFragment } from "../../gql"; -import ConfirmationModal from "../ConfirmationModal/ConfirmationModal"; +import { Close, Description, Dialog, Title } from "../Dialog"; import { Link } from "../Link"; import styles from "./UserEmail.module.css"; @@ -83,8 +84,8 @@ const DeleteButton: React.FC<{ disabled?: boolean; onClick?: () => void }> = ({ ); const DeleteButtonWithConfirmation: React.FC< - ComponentProps -> = ({ onClick, ...rest }) => { + ComponentProps & { email: string } +> = ({ email, onClick, ...rest }) => { const { t } = useTranslation(); const onConfirm = (): void => { onClick?.(); @@ -94,17 +95,32 @@ const DeleteButtonWithConfirmation: React.FC< const onDeny = (): void => {}; return ( - <> - } - onDeny={onDeny} - onConfirm={onConfirm} - > - - {t("frontend.user_email.delete_button_confirmation_modal.body")} - - - + }> + + {t("frontend.user_email.delete_button_confirmation_modal.body")} + + + +
{email}
+
+
+ + + + + + +
+
); }; @@ -151,9 +167,9 @@ const UserEmail: React.FC<{ value={data.email} className={styles.userEmailField} /> - {!isPrimary && (