Skip to content

Commit

Permalink
Modal: New prop for aligning modal to top (#3033)
Browse files Browse the repository at this point in the history
  • Loading branch information
HalvorHaugan authored Jun 27, 2024
1 parent fe93f64 commit 7c644cd
Show file tree
Hide file tree
Showing 6 changed files with 163 additions and 50 deletions.
6 changes: 6 additions & 0 deletions .changeset/tidy-boats-rescue.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@navikt/ds-react": minor
"@navikt/ds-css": minor
---

Modal: New prop `placement` for anchoring the modal to the top of the viewport.
11 changes: 8 additions & 3 deletions @navikt/core/css/modal.css
Original file line number Diff line number Diff line change
Expand Up @@ -59,17 +59,22 @@

@media (min-width: 480px) {
.navds-modal {
max-width: calc(100% - 6px - 2em);
max-width: calc(100% - 2em);
}

.navds-modal--autowidth {
max-width: min(700px, calc(100% - 6px - 2em));
max-width: min(700px, calc(100% - 2em));
}
}

@media (min-height: 480px) {
.navds-modal {
max-height: calc(100% - 6px - 2em);
max-height: calc(100% - 2em);
}

.navds-modal--top {
margin-top: 2em;
max-height: calc(100% - 4em);
}
}

Expand Down
4 changes: 3 additions & 1 deletion @navikt/core/react/src/modal/Modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ export const Modal = forwardRef<HTMLDialogElement, ModalProps>(
onCancel,
closeOnBackdropClick,
width,
placement,
portal,
className,
"aria-labelledby": ariaLabelledby,
Expand Down Expand Up @@ -147,9 +148,10 @@ export const Modal = forwardRef<HTMLDialogElement, ModalProps>(
typeof width === "string" && ["small", "medium"].includes(width);

const mergedClassName = cl("navds-modal", className, {
polyfillClassName: needPolyfill,
[polyfillClassName]: needPolyfill,
"navds-modal--autowidth": !width,
[`navds-modal--${width}`]: isWidthPreset,
"navds-modal--top": placement === "top" && !needPolyfill,
});

const mergedStyle = {
Expand Down
153 changes: 107 additions & 46 deletions @navikt/core/react/src/modal/modal.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ const meta: Meta<typeof Modal> = {
};
export default meta;

const lorem = `Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim
ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
aliquip ex ea commodo consequat. Duis aute irure dolor in
reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
culpa qui officia deserunt mollit anim id est laborum.`;

export const WithUseRef: StoryFn = () => {
const ref = useRef<HTMLDialogElement>(null);
const ref2 = useRef<HTMLDialogElement>(null);
Expand All @@ -37,15 +45,7 @@ export const WithUseRef: StoryFn = () => {
closeOnBackdropClick
>
<Modal.Body>
<BodyLong spacing>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim
ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
aliquip ex ea commodo consequat. Duis aute irure dolor in
reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
culpa qui officia deserunt mollit anim id est laborum.
</BodyLong>
<BodyLong spacing>{lorem}</BodyLong>

{/* Nested modal */}
<Modal
Expand All @@ -72,15 +72,7 @@ export const WithUseRef: StoryFn = () => {
</Modal.Footer>
</Modal>

<BodyLong>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim
ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
aliquip ex ea commodo consequat. Duis aute irure dolor in
reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
culpa qui officia deserunt mollit anim id est laborum.
</BodyLong>
<BodyLong>{lorem}</BodyLong>
</Modal.Body>
<Modal.Footer>
<Button>Primary</Button>
Expand Down Expand Up @@ -152,15 +144,7 @@ WithUseState.parameters = { chromatic: { disable: true } };
export const EmptyHeader: StoryFn = () => (
<Modal open onClose={() => null} aria-label="Modal with empty header">
<Modal.Header />
<Modal.Body>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
commodo consequat. Duis aute irure dolor in reprehenderit in voluptate
velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat
cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id
est laborum.
</Modal.Body>
<Modal.Body>{lorem}</Modal.Body>
</Modal>
);

Expand Down Expand Up @@ -216,6 +200,99 @@ export const Large800: StoryFn = () => (
);
Large800.storyName = "Size = 800px";

export const PlacementTopShort: StoryFn = () => (
<div style={{ width: "100vw", height: "100vh" }}>
<style>{`#storybook-root { padding: 0 !important }`}</style>
<Modal
open
onClose={() => null}
placement="top"
header={{ heading: "placement = top" }}
>
<Modal.Body>
This modal should be top anchored, except on mobile and old browsers.
</Modal.Body>
</Modal>
</div>
);
PlacementTopShort.parameters = {
chromatic: {
modes: {
mobile: {
viewport: {
width: 400,
height: 400,
},
},
desktop: {
viewport: {
width: 1024,
height: 600,
},
},
},
},
};

export const PlacementTopLong: StoryFn = () => {
const ref = useRef<HTMLDialogElement>(null);
return (
<div style={{ width: "100vw", height: "100vh" }}>
<style>{`#storybook-root { padding: 0 !important }`}</style>
<Modal
open
onClose={() => null}
placement="top"
header={{ heading: "placement = top" }}
>
<Modal.Body>
<BodyLong spacing>
This modal should be top anchored, except on mobile and old
browsers.
</BodyLong>
<BodyLong spacing>{lorem}</BodyLong>
<BodyLong>{lorem}</BodyLong>

<Modal
ref={ref}
onClose={() => null}
placement="top"
header={{ heading: "placement = top (Nested)" }}
>
<Modal.Body>
This modal should also be top anchored, except on mobile and old
browsers.
</Modal.Body>
</Modal>
</Modal.Body>
<Modal.Footer>
<Button onClick={() => ref.current?.showModal()}>
Open nested modal
</Button>
</Modal.Footer>
</Modal>
</div>
);
};
PlacementTopLong.parameters = {
chromatic: {
modes: {
mobile: {
viewport: {
width: 400,
height: 400,
},
},
desktop: {
viewport: {
width: 1024,
height: 600,
},
},
},
},
};

export const WithTooltip: StoryFn = () => {
const ref = useRef<HTMLDialogElement>(null);

Expand Down Expand Up @@ -295,7 +372,7 @@ export const WithSrOnlyElement: StoryFn = () => (
);

export const ChromaticViewportTesting: StoryFn = () => (
<div id="modal-story-wrapper" style={{ width: "100vw", height: "100vh" }}>
<div style={{ width: "100vw", height: "100vh" }}>
<style>{`#storybook-root { padding: 0 !important }`}</style>
<Modal
open
Expand All @@ -306,24 +383,8 @@ export const ChromaticViewportTesting: StoryFn = () => (
<BodyLong spacing>
This story is tailored for testing the breakpoints with Chromatic.
</BodyLong>
<BodyLong spacing>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad
minim veniam, quis nostrud exercitation ullamco laboris nisi ut
aliquip ex ea commodo consequat.
</BodyLong>
<BodyLong spacing>
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum
dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
proident, sunt in culpa qui officia deserunt mollit anim id est
laborum.
</BodyLong>
<BodyLong>
Sed ut perspiciatis unde omnis iste natus error sit voluptatem
accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae
ab illo inventore veritatis et quasi architecto beatae vitae dicta
sunt explicabo.
</BodyLong>
<BodyLong spacing>{lorem}</BodyLong>
<BodyLong>{lorem}</BodyLong>
</Modal.Body>
<Modal.Footer>
<Button>Primary</Button>
Expand Down
5 changes: 5 additions & 0 deletions @navikt/core/react/src/modal/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@ interface ModalPropsBase extends React.DialogHTMLAttributes<HTMLDialogElement> {
* @default fit-content (up to 700px)
* */
width?: "medium" | "small" | number | `${number}${string}`;
/**
* Where to place the modal in the viewport. (Will always be centered on mobile and old browsers.)
* @default "center"
*/
placement?: "top" | "center";
/**
* Lets you render the modal into a different part of the DOM.
* Will use `rootElement` from `Provider` if defined, otherwise `document.body`.
Expand Down
34 changes: 34 additions & 0 deletions aksel.nav.no/website/pages/eksempler/modal/placement-top.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { useRef } from "react";
import { BodyLong, Button, Modal } from "@navikt/ds-react";
import { withDsExample } from "@/web/examples/withDsExample";

const Example = () => {
const ref = useRef<HTMLDialogElement>(null);

return (
<div className="py-32">
<Button onClick={() => ref.current?.showModal()}>Åpne modal</Button>

<Modal ref={ref} header={{ heading: "Overskrift" }} placement="top">
<Modal.Body>
<BodyLong>
Culpa aliquip ut cupidatat laborum minim quis ex in aliqua.
</BodyLong>
</Modal.Body>
</Modal>
</div>
);
};

// EXAMPLES DO NOT INCLUDE CONTENT BELOW THIS LINE
export default withDsExample(Example);

/* Storybook story */
export const Demo = {
render: Example,
};

export const args = {
index: 6,
desc: "Bruk placement='top' hvis høyden kan endre seg (dynamisk innhold).",
};

0 comments on commit 7c644cd

Please sign in to comment.