Skip to content

Commit e0dc126

Browse files
committed
Introduce color property to the Modal component (#468)
`Modal` can now be colored via the `color` prop, using a value from the Feedback Colors collection.
1 parent 4ead76d commit e0dc126

File tree

9 files changed

+184
-3
lines changed

9 files changed

+184
-3
lines changed

src/components/Modal/Modal.jsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { createPortal } from 'react-dom';
99
import { classNames } from '../../helpers/classNames';
1010
import { transferProps } from '../../helpers/transferProps';
1111
import { withGlobalProps } from '../../providers/globalProps';
12+
import { getRootColorClassName } from '../_helpers/getRootColorClassName';
1213
import { dialogOnCancelHandler } from './_helpers/dialogOnCancelHandler';
1314
import { dialogOnClickHandler } from './_helpers/dialogOnClickHandler';
1415
import { dialogOnCloseHandler } from './_helpers/dialogOnCloseHandler';
@@ -21,6 +22,7 @@ import styles from './Modal.module.scss';
2122

2223
const preRender = (
2324
children,
25+
color,
2426
dialogRef,
2527
position,
2628
size,
@@ -32,6 +34,7 @@ const preRender = (
3234
{...transferProps(events)}
3335
className={classNames(
3436
styles.root,
37+
color && getRootColorClassName(color, styles),
3538
getSizeClassName(size, styles),
3639
getPositionClassName(position, styles),
3740
)}
@@ -48,6 +51,7 @@ export const Modal = ({
4851
autoFocus,
4952
children,
5053
closeButtonRef,
54+
color,
5155
dialogRef,
5256
portalId,
5357
position,
@@ -107,6 +111,7 @@ export const Modal = ({
107111
if (portalId === null) {
108112
return preRender(
109113
children,
114+
color,
110115
internalDialogRef,
111116
position,
112117
size,
@@ -118,6 +123,7 @@ export const Modal = ({
118123
return createPortal(
119124
preRender(
120125
children,
126+
color,
121127
internalDialogRef,
122128
position,
123129
size,
@@ -135,6 +141,7 @@ Modal.defaultProps = {
135141
autoFocus: true,
136142
children: null,
137143
closeButtonRef: null,
144+
color: undefined,
138145
dialogRef: null,
139146
portalId: null,
140147
position: 'center',
@@ -180,6 +187,11 @@ Modal.propTypes = {
180187
// eslint-disable-next-line react/forbid-prop-types
181188
current: PropTypes.any,
182189
}),
190+
/**
191+
* Color to clarify importance and meaning of the modal. Implements
192+
* [Feedback color collection](/docs/foundation/collections#colors).
193+
*/
194+
color: PropTypes.oneOf(['success', 'warning', 'danger', 'help', 'info', 'note']),
183195
/**
184196
* Reference to dialog element
185197
*/

src/components/Modal/Modal.module.scss

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
@use "../../styles/theme/typography";
88
@use "../../styles/tools/accessibility";
99
@use "../../styles/tools/breakpoint";
10+
@use "../../styles/tools/collections";
1011
@use "../../styles/tools/reset";
1112
@use "../../styles/tools/spacing";
1213
@use "animations";
@@ -82,4 +83,14 @@
8283
top: var(--rui-local-outer-spacing);
8384
bottom: auto;
8485
}
86+
87+
@each $color in settings.$colors {
88+
@include collections.generate-class(
89+
$prefix: "rui-",
90+
$component-name: "Modal",
91+
$variant-name: "color",
92+
$variant-value: $color,
93+
$properties: settings.$themeable-properties,
94+
);
95+
}
8596
}

src/components/Modal/ModalBody.module.scss

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,24 @@
1+
// 1. Intentionally do not provide a fallback value for the border color. Setting a fallback value (e.g. `transparent`)
2+
// will result in the border being skewed at both ends.
3+
4+
@use "settings";
5+
16
@layer components.modal {
27
.root {
38
flex: 1 1 auto;
9+
border-inline: settings.$border-width solid var(--rui-local-border-color); // 1.
10+
11+
&:first-child {
12+
border-top: settings.$border-width solid var(--rui-local-border-color); // 1.
13+
border-top-left-radius: settings.$border-radius;
14+
border-top-right-radius: settings.$border-radius;
15+
}
16+
17+
&:last-child {
18+
border-bottom: settings.$border-width solid var(--rui-local-border-color); // 1.
19+
border-bottom-right-radius: settings.$border-radius;
20+
border-bottom-left-radius: settings.$border-radius;
21+
}
422
}
523

624
.isRootScrollingAuto,

src/components/Modal/ModalFooter.module.scss

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
// 1. Intentionally do not provide a fallback value for the border color. Setting a fallback value (e.g. `transparent`)
2+
// will result in the border being skewed at both ends.
3+
14
@use "settings";
25
@use "theme";
36

@@ -9,10 +12,11 @@
912
gap: theme.$footer-gap;
1013
align-items: center;
1114
padding: theme.$padding-y theme.$padding-x;
12-
border-top: theme.$separator-width solid theme.$separator-color;
15+
border: settings.$border-width solid var(--rui-local-border-color); // 1.
16+
border-top: theme.$separator-width solid var(--rui-local-border-color, #{theme.$separator-color});
1317
border-bottom-right-radius: settings.$border-radius;
1418
border-bottom-left-radius: settings.$border-radius;
15-
background: theme.$footer-background;
19+
background: var(--rui-local-background-color, #{theme.$footer-background});
1620
}
1721

1822
.isRootJustifiedToStart {

src/components/Modal/ModalHeader.module.scss

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
// 1. Intentionally do not provide a fallback value for the border color. Setting a fallback value (e.g. `transparent`)
2+
// will result in the border being skewed at both ends.
3+
4+
@use "settings";
15
@use "theme";
26

37
@layer components.modal {
@@ -7,7 +11,10 @@
711
gap: theme.$header-gap;
812
align-items: baseline;
913
padding: theme.$padding-y theme.$padding-x;
10-
border-bottom: theme.$separator-width solid theme.$separator-color;
14+
border: settings.$border-width solid var(--rui-local-border-color); // 1.
15+
border-bottom: theme.$separator-width solid var(--rui-local-border-color, #{theme.$separator-color});
16+
border-top-left-radius: settings.$border-radius;
17+
border-top-right-radius: settings.$border-radius;
1118
}
1219

1320
.isRootJustifiedToStart {

src/components/Modal/README.md

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -690,6 +690,90 @@ React.createElement(() => {
690690
});
691691
```
692692

693+
## Color Variants
694+
695+
Modal can be colored using the `color` prop. The `color` prop implements the
696+
[Feedback color collection](/docs/foundation/collections#colors)
697+
and is applied to the border of the modal and the modal footer.
698+
699+
```docoff-react-preview
700+
React.createElement(() => {
701+
const [modalOpen, setModalOpen] = React.useState(false);
702+
const [modalColor, setModalColor] = React.useState('success');
703+
const modalCloseButtonRef = React.useRef();
704+
{/*
705+
The `preventScrollUnderneath` feature is necessary for Modals to work in
706+
React UI docs. You may not need it in your application.
707+
*/}
708+
return (
709+
<GlobalPropsProvider globalProps={{
710+
Modal: { preventScrollUnderneath: window.document.documentElement }
711+
}}>
712+
<Button
713+
label="Launch modal with color options"
714+
onClick={() => setModalOpen(true)}
715+
/>
716+
<div>
717+
{modalOpen && (
718+
<Modal
719+
closeButtonRef={modalCloseButtonRef}
720+
color={modalColor}
721+
>
722+
<ModalHeader>
723+
<ModalTitle>Modal color</ModalTitle>
724+
<ModalCloseButton onClick={() => setModalOpen(false)} />
725+
</ModalHeader>
726+
<ModalBody>
727+
<ModalContent>
728+
<Radio
729+
label="Modal color"
730+
onChange={(e) => setModalColor(e.target.value)}
731+
options={[
732+
{
733+
label: 'success',
734+
value: 'success',
735+
},
736+
{
737+
label: 'warning',
738+
value: 'warning',
739+
},
740+
{
741+
label: 'danger',
742+
value: 'danger',
743+
},
744+
{
745+
label: 'info',
746+
value: 'info',
747+
},
748+
{
749+
label: 'help',
750+
value: 'help',
751+
},
752+
{
753+
label: 'note',
754+
value: 'note',
755+
},
756+
]}
757+
value={modalColor}
758+
/>
759+
</ModalContent>
760+
</ModalBody>
761+
<ModalFooter>
762+
<Button
763+
color={modalColor}
764+
label="Close"
765+
onClick={() => setModalOpen(false)}
766+
ref={modalCloseButtonRef}
767+
/>
768+
</ModalFooter>
769+
</Modal>
770+
)}
771+
</div>
772+
</GlobalPropsProvider>
773+
);
774+
});
775+
```
776+
693777
## Mouse and Keyboard Control
694778

695779
Modal can be controlled either by mouse or keyboard. To enhance user
@@ -1207,6 +1291,21 @@ accessibility.
12071291
| `--rui-Modal--fullscreen__height` | Height of fullscreen modal |
12081292
| `--rui-Modal__animation__duration` | Duration of animation used (when opening modal) |
12091293

1294+
### Theming Variants
1295+
1296+
It's possible to adjust the theme of specific color variant. Naming convention
1297+
looks as follows:
1298+
1299+
`--rui-Modal--<COLOR>__<PROPERTY>`
1300+
1301+
Where:
1302+
1303+
- `<COLOR>` is a value from supported
1304+
[color collections](/docs/foundation/collections#colors)
1305+
(check [color variants](#color-variants) and [API](#api) to see which
1306+
collections are supported),
1307+
- `<PROPERTY>` is one of `border-color` or `background-color`.
1308+
12101309
[button-attributes]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#attributes
12111310
[controlled-components]: /docs/getting-started/usage#foundation-css
12121311
[dialog-attributes]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog#attributes

src/components/Modal/__tests__/Modal.test.jsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
within,
77
} from '@testing-library/react';
88
import userEvent from '@testing-library/user-event';
9+
import { feedbackColorPropTest } from '../../../../tests/propTests/feedbackColorPropTest';
910
import { Button } from '../../..';
1011
import { Modal } from '../Modal';
1112
import { ModalBody } from '../ModalBody';
@@ -31,6 +32,7 @@ describe('rendering', () => {
3132
});
3233

3334
it.each([
35+
...feedbackColorPropTest,
3436
[
3537
{ children: <div>content text</div> },
3638
(rootElement) => expect(within(rootElement).getByText('content text')),
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
@use "sass:map";
2+
@use "../../styles/settings/collections";
23
@use "../../styles/theme/borders";
34
@use "../../styles/theme/typography";
45

6+
$border-width: borders.$width;
57
$border-radius: borders.$radius-2;
68
$title-font-size: map.get(typography.$font-size-values, 2);
9+
$colors: collections.$feedback-colors;
10+
$themeable-properties: border-color, background-color;

src/theme.scss

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1014,6 +1014,30 @@
10141014
--rui-Modal--fullscreen__height: 100%;
10151015
--rui-Modal__animation__duration: 0.25s;
10161016

1017+
// Modal: success variant
1018+
--rui-Modal--success__border-color: var(--rui-color-feedback-success);
1019+
--rui-Modal--success__background-color: var(--rui-color-background-success);
1020+
1021+
// Modal: warning variant
1022+
--rui-Modal--warning__border-color: var(--rui-color-feedback-warning);
1023+
--rui-Modal--warning__background-color: var(--rui-color-background-warning);
1024+
1025+
// Modal: danger variant
1026+
--rui-Modal--danger__border-color: var(--rui-color-feedback-danger);
1027+
--rui-Modal--danger__background-color: var(--rui-color-background-danger);
1028+
1029+
// Modal: info variant
1030+
--rui-Modal--info__border-color: var(--rui-color-feedback-info);
1031+
--rui-Modal--info__background-color: var(--rui-color-background-info);
1032+
1033+
// Modal: help variant
1034+
--rui-Modal--help__border-color: var(--rui-color-feedback-help);
1035+
--rui-Modal--help__background-color: var(--rui-color-background-help);
1036+
1037+
// Modal: note variant
1038+
--rui-Modal--note__border-color: var(--rui-color-feedback-note);
1039+
--rui-Modal--note__background-color: var(--rui-color-background-note);
1040+
10171041
//
10181042
// Paper
10191043
// =====

0 commit comments

Comments
 (0)