Skip to content

Commit

Permalink
feat(Dialog): add verticalAlignment property with top alignment s…
Browse files Browse the repository at this point in the history
…upport (#4190)

This is useful for when a Dialog contains changes to its content, where
the `center` aligned Dialog will move its placement on the screen. A top
aligned Dialog will keep its position.


[Example](https://eufemia-git-feat-dialog-placement-eufemia.vercel.app/uilib/components/dialog/#top-aligned-dialog)

---------

Co-authored-by: Anders <[email protected]>
  • Loading branch information
tujoworker and langz authored Oct 29, 2024
1 parent 9b38904 commit 3ace8b0
Show file tree
Hide file tree
Showing 19 changed files with 190 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,19 @@ export const DialogExampleHelpButton = () => (
</ComponentBox>
)

export const DialogExampleVerticalAlignment = () => (
<ComponentBox data-visual-test="dialog-vertical-alignment">
<Dialog
title="Vertical alignment top"
verticalAlignment="top"
triggerAttributes={{
text: 'Vertical alignment',
}}
modalContent="The Dialog component is a Modal aligned at the top of the screen. The Dialog has similar functionality to a traditional popup window and is mostly used for informational purposes."
/>
</ComponentBox>
)

export const DialogExampleFullscreen = () => (
<ComponentBox data-visual-test="dialog-fullscreen">
<Dialog
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
DialogExampleDefault,
DialogExampleHelpButton,
DialogExampleFullscreen,
DialogExampleVerticalAlignment,
DialogExampleDelayClose,
DialogExampleCustomTrigger,
DialogExampleProgressIndicator,
Expand All @@ -32,6 +33,10 @@ import {

<DialogExampleHelpButton />

### Top aligned Dialog

<DialogExampleVerticalAlignment />

### Dialog with custom trigger

<DialogExampleCustomTrigger />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,6 @@
| Properties | Description |
| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `variant` | _(optional)_ The dialog variant. Can either be `information` or `confirmation`. Defaults to `information`. |
| `title` | _(optional)_ The dialog title. Displays on the very top of the content. |
| `minWidth` | _(optional)_ The minimum Dialog content width, defined by a CSS width value like `50vw` (50% of the viewport). Be careful on using fixed `minWidth` so you don't break responsiveness. Defaults to `30rem` (average width is set to `60vw`). |
| `maxWidth` | _(optional)_ The maximum Dialog content width, defined by a CSS width value like `20rem`. Defaults to `60rem` (average width is set to `60vw`). |
| `className` | _(optional)_ Give the Dialog content a class name (maps to `dnb-dialog`). |
| `spacing` | _(optional)_ If set to `false` then the dialog content will be shown without any spacing. Defaults to `true`. |
| `preventCoreStyle` | _(optional)_ By default the dialog content gets added the core style class `dnb-core-style`. Use `false` to disable this behavior. |
| `navContent` | _(optional)_ The content which will appear in the navigation, above the header, and side-by-side the close button. |
| `headerContent` | _(optional)_ The content which will appear in the header of the dialog. |
| `modalContent` | _(optional)_ The content which will appear when triggering the dialog. |
| `description` | _(optional)_ A description will be positioned below the title, but before the content. Used for Dialog variant `confirmation` to further describe what the actions will do. |
| `alignContent` | _(optional)_ Define the inner horizontal alignment of the content. Can be set to `left`, `center`, `right` and `centered`. If `centered`, then the content will also be centered vertically. Defaults to `left`. |
| `fullscreen` | _(optional)_ If set to `true` then the dialog content will be shown as fullscreen, without showing the original content behind. Can be set to `false` to omit the auto fullscreen. Defaults to `auto`. |
| `icon` | _(optional)_ An icon to display at the top of the component. Should be of size medium, so make sure you import the `_medium` version of the Eufemia icon. |
| `confirmType` | _(optional)_ For variant confirmation, the dialog is either an informational (`info`) or a warning (`warning`) message. Defaults to 'info'. |
| `declineText` | _(optional)_ For dialog actions, give a custom text for the decline button. |
| `confirmText` | _(optional)_ For dialog actions, give a custom text for the confirmation button. |
| `hideDecline` | _(optional)_ For variant confirmation, hide the default decline button and only show the confirmation button. |
| `hideConfirm` | _(optional)_ For variant confirmation, hide the default confirm button and only show the decline button. |
| `scrollRef` | _(optional)_ To get the scroll Element, pass in your own React ref. |
| `contentRef` | _(optional)_ To get the inner content Element, pass in your own React ref. |
import PropertiesTable from 'dnb-design-system-portal/src/shared/parts/PropertiesTable'
import { DialogProperties } from '@dnb/eufemia/src/components/dialog/DialogDocs'

## Properties

<PropertiesTable props={DialogProperties} />
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
---
---

| Properties | Description |
| -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `containerPlacement` | _(optional)_ Defines on what side the Drawer should be opened. Can be set to `left`, `right`, `top` and `bottom`. Defaults to `right`. |
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
---
---

| Events | Description |
| ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `onOpen` | _(optional)_ This event gets triggered once the modal shows up. Returns the modal id: `{ id }`. |
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
---
---

| Events | Description |
| ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `onOpen` / `on_open` | _(optional)_ This event gets triggered once the modal shows up. Returns the modal id: `{ id }`. |
Expand Down
2 changes: 2 additions & 0 deletions packages/dnb-eufemia/src/components/dialog/Dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ function Dialog(localProps: DialogProps & DialogContentProps) {
dialogTitle,
closeTitle,
spacing,
verticalAlignment,
noAnimation,
noAnimationOnMobile,
animationDuration,
Expand Down Expand Up @@ -100,6 +101,7 @@ function Dialog(localProps: DialogProps & DialogContentProps) {
labelledBy,
disabled,
spacing,
verticalAlignment,
openDelay,
contentId,
dialogTitle,
Expand Down
114 changes: 114 additions & 0 deletions packages/dnb-eufemia/src/components/dialog/DialogDocs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import { PropertiesTableProps } from '../../shared/types'

export const DialogProperties: PropertiesTableProps = {
variant: {
doc: 'The dialog variant. Can either be `information` or `confirmation`. Defaults to `information`.',
type: 'string',
status: 'optional',
},
title: {
doc: 'The dialog title. Displays on the very top of the content.',
type: 'string',
status: 'optional',
},
minWidth: {
doc: "The minimum Dialog content width, defined by a CSS width value like `50vw` (50% of the viewport). Be careful on using fixed `minWidth` so you don't break responsiveness. Defaults to `30rem` (average width is set to `60vw`).",
type: 'string',
status: 'optional',
},
maxWidth: {
doc: 'The maximum Dialog content width, defined by a CSS width value like `20rem`. Defaults to `60rem` (average width is set to `60vw`).',
type: 'string',
status: 'optional',
},
className: {
doc: 'Give the Dialog content a class name (maps to `dnb-dialog`).',
type: 'string',
status: 'optional',
},
spacing: {
doc: 'If set to `false` then the dialog content will be shown without any spacing. Defaults to `true`.',
type: 'boolean',
status: 'optional',
},
preventCoreStyle: {
doc: 'By default the dialog content gets added the core style class `dnb-core-style`. Use `false` to disable this behavior.',
type: 'boolean',
status: 'optional',
},
navContent: {
doc: 'The content which will appear in the navigation, above the header, and side-by-side the close button.',
type: 'React.Node',
status: 'optional',
},
headerContent: {
doc: 'The content which will appear in the header of the dialog.',
type: 'React.Node',
status: 'optional',
},
modalContent: {
doc: 'The content which will appear when triggering the dialog.',
type: 'React.Node',
status: 'optional',
},
description: {
doc: 'A description will be positioned below the title, but before the content. Used for Dialog variant `confirmation` to further describe what the actions will do.',
type: 'string',
status: 'optional',
},
verticalAlignment: {
doc: 'Define the vertical alignment of the container. Can be set to `top` or `center`. Defaults to `center`.',
type: 'string',
status: 'optional',
},
alignContent: {
doc: 'Define the inner horizontal alignment of the content. Can be set to `left`, `center`, `right` and `centered`. If `centered`, then the content will also be centered vertically. Defaults to `left`.',
type: 'string',
status: 'optional',
},
fullscreen: {
doc: 'If set to `true` then the dialog content will be shown as fullscreen, without showing the original content behind. Can be set to `false` to omit the auto fullscreen. Defaults to `auto`.',
type: 'boolean',
status: 'optional',
},
icon: {
doc: 'An icon to display at the top of the component. Should be of size medium, so make sure you import the `_medium` version of the Eufemia icon.',
type: 'React.Node',
status: 'optional',
},
confirmType: {
doc: 'For variant confirmation, the dialog is either an informational (`info`) or a warning (`warning`) message. Defaults to `info`.',
type: 'string',
status: 'optional',
},
declineText: {
doc: 'For dialog actions, give a custom text for the decline button.',
type: 'string',
status: 'optional',
},
confirmText: {
doc: 'For dialog actions, give a custom text for the confirmation button.',
type: 'string',
status: 'optional',
},
hideDecline: {
doc: 'For variant confirmation, hide the default decline button and only show the confirmation button.',
type: 'boolean',
status: 'optional',
},
hideConfirm: {
doc: 'For variant confirmation, hide the default confirm button and only show the decline button.',
type: 'boolean',
status: 'optional',
},
scrollRef: {
doc: 'To get the scroll Element, pass in your own React ref.',
type: 'React.Ref',
status: 'optional',
},
contentRef: {
doc: 'To get the inner content Element, pass in your own React ref.',
type: 'React.Ref',
status: 'optional',
},
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,19 @@ describe.each(['ui', 'sbanken'])('Dialog for %s', (themeName) => {
expect(screenshot).toMatchImageSnapshot()
})

it('have to match a top aligned dialog', async () => {
const screenshot = await makeScreenshot({
selector: 'div#dnb-modal-root', // only to make sure we have a valid selector
simulate: 'click',
simulateSelector:
'[data-visual-test="dialog-vertical-alignment"] button:first-of-type',
simulateAfter: { keypress: 'Escape' },
screenshotSelector: '.dnb-modal__content',
rootClassName: 'hide-page-content',
})
expect(screenshot).toMatchImageSnapshot()
})

it('have to match the dialog fullscreen window', async () => {
const screenshot = await makeScreenshot({
selector: 'div#dnb-modal-root', // only to make sure we have a valid selector
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,16 @@ describe('Dialog', () => {
)
})

it('will set correct class when verticalAlignment is set to top', () => {
render(<Dialog verticalAlignment="top" />)

fireEvent.click(document.querySelector('button'))

expect(document.querySelector('.dnb-modal__content')).toHaveClass(
'dnb-modal__vertical-alignment--top'
)
})

it('has to have correct role', () => {
const { rerender } = render(
<Dialog {...props} openState={true}>
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -740,6 +740,10 @@ html[data-dnb-modal-active] {
align-items: flex-end;
justify-content: flex-start;
}
.dnb-modal__vertical-alignment--top {
padding-top: 5vh;
align-items: flex-start;
}
.dnb-modal__overlay {
position: fixed;
z-index: var(--modal-z-index);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -741,6 +741,10 @@ html[data-dnb-modal-active] {
align-items: flex-end;
justify-content: flex-start;
}
.dnb-modal__vertical-alignment--top {
padding-top: 5vh;
align-items: flex-start;
}
.dnb-modal__overlay {
position: fixed;
z-index: var(--modal-z-index);
Expand Down
3 changes: 3 additions & 0 deletions packages/dnb-eufemia/src/components/modal/Modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ class Modal extends React.PureComponent<
max_width: null,
align_content: 'left',
container_placement: null,
vertical_alignment: null,
open_state: null,
direct_dom_return: false,
root_id: 'root',
Expand Down Expand Up @@ -444,6 +445,7 @@ class Modal extends React.PureComponent<
header_content = null,
bar_content = null,
bypass_invalidation_selectors = null,
vertical_alignment = 'center',

id, // eslint-disable-line
open_state, // eslint-disable-line
Expand Down Expand Up @@ -526,6 +528,7 @@ class Modal extends React.PureComponent<
focus_selector={focus_selector}
modal_content={modal_content}
header_content={header_content}
vertical_alignment={vertical_alignment}
bar_content={bar_content}
bypass_invalidation_selectors={bypass_invalidation_selectors}
close={this.close}
Expand Down
2 changes: 2 additions & 0 deletions packages/dnb-eufemia/src/components/modal/ModalContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,7 @@ export default class ModalContent extends React.PureComponent<
no_animation_on_mobile = false,
fullscreen = 'auto',
container_placement = 'right',
vertical_alignment = 'center',
close,
content_class,
overlay_class,
Expand Down Expand Up @@ -422,6 +423,7 @@ export default class ModalContent extends React.PureComponent<
container_placement
? `dnb-modal__content--${container_placement || 'right'}`
: null,
`dnb-modal__vertical-alignment--${vertical_alignment}`,
getThemeClasses(this.context?.theme),
content_class
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -733,6 +733,10 @@ html[data-dnb-modal-active] {
align-items: flex-end;
justify-content: flex-start;
}
.dnb-modal__vertical-alignment--top {
padding-top: 5vh;
align-items: flex-start;
}
.dnb-modal__overlay {
position: fixed;
z-index: var(--modal-z-index);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,11 @@ html[data-dnb-modal-active] {
}
}

&__vertical-alignment--top {
padding-top: 5vh;
align-items: flex-start;
}

&__overlay {
position: fixed;
z-index: var(--modal-z-index);
Expand Down
5 changes: 5 additions & 0 deletions packages/dnb-eufemia/src/components/modal/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,11 @@ export interface ModalContentProps {
*/
container_placement?: 'left' | 'right' | 'top' | 'bottom'

/**
* Define the vertical alignment of the container. Can be set to `top` or `center`. Defaults to `center`.
*/
vertical_alignment?: 'top' | 'center'

/**
* Give the content wrapper a custom class name (maps to `dnb-modal__content`).
*/
Expand Down

0 comments on commit 3ace8b0

Please sign in to comment.