Repository: lightdash/lightdash
Pull request #14516: style: make DashboardUpdateModal a MantineModal
Author: login: joaoviana
Labels: name: released
Created: 2025-04-23T16:57:53Z | Merged: 2025-04-24T08:58:46Z

<!-- Thanks so much for your PR, your contribution is appreciated! ❤️ -->

Closes: N/A

### Description:

Updated this component to use MantineModal
Added a storybook example with dev notes - lmk if this is not the right place to do this.

![Screenshot 2025-04-23 at 17.57.04.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/DJhzIQbRJqTJiKN3mUjT/97e43ae6-f2fd-435e-bcbb-70aaad2a1296.png)

### Reviewer actions

- [ ] I have manually tested the changes in the preview environment
- [ ] I have reviewed the code
- [ ] I understand that "request changes" will block this PR from merging



Diff:
diff --git a/packages/e2e/cypress/e2e/app/savedDashboards.cy.ts b/packages/e2e/cypress/e2e/app/savedDashboards.cy.ts
index d7e70b7843a82..000d39e8efa31 100644
--- a/packages/e2e/cypress/e2e/app/savedDashboards.cy.ts
+++ b/packages/e2e/cypress/e2e/app/savedDashboards.cy.ts
@@ -37,9 +37,7 @@ describe('Dashboard List', () => {
         cy.contains('tr', 'Untitled dashboard').find('button').click();
         // click on rename
         cy.findByRole('menuitem', { name: 'Rename' }).click();
-        cy.findByLabelText('Enter a memorable name for your dashboard *')
-            .clear()
-            .type('e2e dashboard');
+        cy.findByLabelText('Name *').clear().type('e2e dashboard');
         // click on save
         cy.findByRole('button', { name: 'Save' }).click();
 
diff --git a/packages/frontend/src/components/common/modal/DashboardUpdateModal.tsx b/packages/frontend/src/components/common/modal/DashboardUpdateModal.tsx
index cbcb156bbf38b..b43dc59d93700 100644
--- a/packages/frontend/src/components/common/modal/DashboardUpdateModal.tsx
+++ b/packages/frontend/src/components/common/modal/DashboardUpdateModal.tsx
@@ -2,21 +2,23 @@ import { type Dashboard } from '@lightdash/common';
 import {
     Button,
     Group,
-    Modal,
     Stack,
     TextInput,
     Textarea,
-    Title,
     type ModalProps,
 } from '@mantine/core';
 import { useForm } from '@mantine/form';
+import { IconLayoutDashboard } from '@tabler/icons-react';
 import { useEffect, type FC } from 'react';
 import {
     useDashboardQuery,
     useUpdateDashboard,
 } from '../../../hooks/dashboard/useDashboard';
+import MantineModal from '../MantineModal';
 
-interface DashboardUpdateModalProps extends ModalProps {
+interface DashboardUpdateModalProps {
+    opened: ModalProps['opened'];
+    onClose: ModalProps['onClose'];
     uuid: string;
     onConfirm?: () => void;
 }
@@ -62,14 +64,35 @@ const DashboardUpdateModal: FC<DashboardUpdateModalProps> = ({
     });
 
     return (
-        <Modal
-            title={<Title order={4}>Update Dashboard</Title>}
+        <MantineModal
+            title="Update Dashboard"
             {...modalProps}
+            icon={IconLayoutDashboard}
+            actions={
+                <Group position="right">
+                    <Button variant="outline" onClick={modalProps.onClose}>
+                        Cancel
+                    </Button>
+
+                    <Button
+                        disabled={!form.isValid()}
+                        loading={isUpdating}
+                        type="submit"
+                        form="update-dashboard"
+                    >
+                        Save
+                    </Button>
+                </Group>
+            }
         >
-            <form title="Update Dashboard" onSubmit={handleConfirm}>
-                <Stack spacing="lg" pt="sm">
+            <form
+                id="update-dashboard"
+                title="Update Dashboard"
+                onSubmit={handleConfirm}
+            >
+                <Stack spacing="lg">
                     <TextInput
-                        label="Enter a memorable name for your dashboard"
+                        label="Name"
                         required
                         placeholder="eg. KPI Dashboards"
                         disabled={isUpdating}
@@ -84,23 +107,9 @@ const DashboardUpdateModal: FC<DashboardUpdateModalProps> = ({
                         maxRows={3}
                         {...form.getInputProps('description')}
                     />
-
-                    <Group position="right" mt="sm">
-                        <Button variant="outline" onClick={modalProps.onClose}>
-                            Cancel
-                        </Button>
-
-                        <Button
-                            disabled={!form.isValid()}
-                            loading={isUpdating}
-                            type="submit"
-                        >
-                            Save
-                        </Button>
-                    </Group>
                 </Stack>
             </form>
-        </Modal>
+        </MantineModal>
     );
 };
 
diff --git a/packages/frontend/src/stories/Modal.stories.tsx b/packages/frontend/src/stories/Modal.stories.tsx
index 5b0dcf3ad03fb..d8e16bd386193 100644
--- a/packages/frontend/src/stories/Modal.stories.tsx
+++ b/packages/frontend/src/stories/Modal.stories.tsx
@@ -1,7 +1,18 @@
 import type { StoryObj } from '@storybook/react';
 
-import { Alert, Button, Flex, Text } from '@mantine/core';
-import { IconTrash } from '@tabler/icons-react';
+import {
+    Alert,
+    Button,
+    Flex,
+    Paper,
+    Stack,
+    Text,
+    TextInput,
+    Title,
+} from '@mantine/core';
+import { useForm } from '@mantine/form';
+import { IconSettings, IconTrash } from '@tabler/icons-react';
+import { useState } from 'react';
 import MantineModal from '../components/common/MantineModal';
 
 export default {
@@ -55,3 +66,74 @@ export const Default: Story = {
         modalActionsProps: {},
     },
 };
+
+export const WithForm: Story = {
+    render: () => {
+        const [opened, setOpened] = useState(false);
+
+        const form = useForm({
+            initialValues: {
+                name: '',
+            },
+        });
+
+        const handleSubmit = form.onSubmit((values) => {
+            console.log('Form submitted with values:', values);
+            setOpened(false);
+        });
+
+        return (
+            <>
+                <Stack>
+                    <Paper withBorder p="md">
+                        <Title order={3}>Dev notes</Title>
+                        <Text>
+                            Make sure you pass a form "id" to the form
+                            <br />
+                            Make sure you pass a "form" attribute to the submit
+                            button
+                        </Text>
+                    </Paper>
+                    <Button onClick={() => setOpened(true)}>
+                        Open modal with form
+                    </Button>
+                </Stack>
+
+                <MantineModal
+                    title="Form Example"
+                    opened={opened}
+                    onClose={() => setOpened(false)}
+                    icon={IconSettings}
+                    actions={
+                        <Flex gap="sm">
+                            <Button
+                                variant="default"
+                                h={32}
+                                onClick={() => setOpened(false)}
+                            >
+                                Cancel
+                            </Button>
+                            <Button
+                                h={32}
+                                type="submit"
+                                form="modal-form-example"
+                                disabled={!form.isValid()}
+                            >
+                                Submit
+                            </Button>
+                        </Flex>
+                    }
+                >
+                    <form id="modal-form-example" onSubmit={handleSubmit}>
+                        <TextInput
+                            label="Name"
+                            required
+                            placeholder="Enter a name"
+                            {...form.getInputProps('name')}
+                        />
+                    </form>
+                </MantineModal>
+            </>
+        );
+    },
+};


Reviews:
user: login: notgiorgi
body: ✨
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-24T07:08:28Z
user: login: joaoviana
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-24T08:58:39Z

Review comments:
diff_hunk: @@ -62,14 +64,35 @@ const DashboardUpdateModal: FC<DashboardUpdateModalProps> = ({
     });
 
     return (
-        <Modal
-            title={<Title order={4}>Update Dashboard</Title>}
+        <MantineModal
+            title="Update Dashboard"
             {...modalProps}
+            icon={IconLayoutDashboard}
path: packages/frontend/src/components/common/modal/DashboardUpdateModal.tsx
user: login: notgiorgi
body: You don't have to do it now but check this out: https://github.com/lightdash/lightdash/issues/14520
created_at: 2025-04-24T07:08:14Z
author_association: CONTRIBUTOR
diff_hunk: @@ -37,9 +37,7 @@ describe('Dashboard List', () => {
         cy.contains('tr', 'Untitled dashboard').find('button').click();
         // click on rename
         cy.findByRole('menuitem', { name: 'Rename' }).click();
-        cy.findByLabelText('Enter a memorable name for your dashboard *')
-            .clear()
-            .type('e2e dashboard');
+        cy.findByLabelText('Name *').clear().type('e2e dashboard');
path: packages/e2e/cypress/e2e/app/savedDashboards.cy.ts
user: login: joaoviana
body: input label doesn't need to be "enter..." when the user is editing a dashboard - feels unnecessary
created_at: 2025-04-24T08:58:39Z
author_association: CONTRIBUTOR

PR comments:
user: login: joaoviana
created_at: 2025-04-23T16:58:14Z
body: * **#14516** <a href="https://app.graphite.dev/github/pr/lightdash/lightdash/14516?utm_source=stack-comment-icon" target="_blank"><img src="https://static.graphite.dev/graphite-32x32-black.png" alt="Graphite" width="10px" height="10px"/></a> 👈 <a href="https://app.graphite.dev/github/pr/lightdash/lightdash/14516?utm_source=stack-comment-view-in-graphite" target="_blank">(View in Graphite)</a>
* `main`


<h2></h2>

This stack of pull requests is managed by <a href="https://graphite.dev?utm-source=stack-comment"><b>Graphite</b></a>. Learn more about <a href="https://stacking.dev/?utm_source=stack-comment">stacking</a>.
<!-- Current dependencies on/for this PR: -->
author_association: CONTRIBUTOR
user: login: lightdash-bot
created_at: 2025-04-24T09:22:09Z
body: :tada: This PR is included in version 0.1596.3 :tada:

The release is available on:
- `0.1596.3`
- [GitHub release](https://github.com/lightdash/lightdash/releases/tag/0.1596.3)

Your **[semantic-release](https://github.com/semantic-release/semantic-release)** bot :package::rocket:
author_association: COLLABORATOR