Skip to content

Commit ef7f010

Browse files
committed
feat(Modal): labelClose prop is now required when onClose is defined
BREAKING CHANGE: the labelClose prop no longer has a default value. It is now required whenever onClose is defined and hasCloseButton is not explicitly set to false (default value is true).
1 parent 52cac3d commit ef7f010

File tree

6 files changed

+60
-40
lines changed

6 files changed

+60
-40
lines changed

packages/orbit-components/src/Modal/Modal.ct-story.tsx

+14-4
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ const content = "Lorem ispum dolor sit amet.";
1313

1414
export function ModalVisualDefaultStory({ size = SIZES.NORMAL, isMobileFullPage = false }) {
1515
return (
16-
<Modal onClose={() => {}} size={size} isMobileFullPage={isMobileFullPage}>
16+
<Modal onClose={() => {}} size={size} isMobileFullPage={isMobileFullPage} labelClose="Close">
1717
<ModalHeader
1818
title="Normal header"
1919
illustration={<Illustration name="AppKiwi" size="small" />}
@@ -36,7 +36,7 @@ export function ModalVisualDefaultStory({ size = SIZES.NORMAL, isMobileFullPage
3636

3737
export function ModalVisualMobileHeader() {
3838
return (
39-
<Modal onClose={() => {}} size={SIZES.NORMAL} mobileHeader>
39+
<Modal onClose={() => {}} size={SIZES.NORMAL} mobileHeader labelClose="Close">
4040
<ModalHeader
4141
title="Suppressed header"
4242
illustration={<Illustration name="AppKiwi" size="small" />}
@@ -59,7 +59,12 @@ export function ModalVisualMobileHeader() {
5959

6060
export function ModalVisualNoHeaderNoFooter({ isMobileFullPage = false }) {
6161
return (
62-
<Modal onClose={() => {}} size={SIZES.NORMAL} isMobileFullPage={isMobileFullPage}>
62+
<Modal
63+
onClose={() => {}}
64+
size={SIZES.NORMAL}
65+
isMobileFullPage={isMobileFullPage}
66+
labelClose="Close"
67+
>
6368
<ModalSection>
6469
<Text>No Header nor Footer modal</Text>
6570
</ModalSection>
@@ -69,7 +74,12 @@ export function ModalVisualNoHeaderNoFooter({ isMobileFullPage = false }) {
6974

7075
export function ModalVisualHeaderOnly({ isMobileFullPage = false }) {
7176
return (
72-
<Modal onClose={() => {}} size={SIZES.NORMAL} isMobileFullPage={isMobileFullPage}>
77+
<Modal
78+
onClose={() => {}}
79+
size={SIZES.NORMAL}
80+
isMobileFullPage={isMobileFullPage}
81+
labelClose="Close"
82+
>
7383
<ModalHeader
7484
title="Normal header"
7585
illustration={<Illustration name="AppKiwi" size="small" />}

packages/orbit-components/src/Modal/Modal.stories.tsx

+4-3
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ export const RemovableSections: Story = {
236236
const { Container, onClose, triggerRef } = useModal();
237237
return (
238238
<Container>
239-
<Modal triggerRef={triggerRef} onClose={onClose}>
239+
<Modal triggerRef={triggerRef} onClose={onClose} labelClose="Close">
240240
<ModalHeader
241241
title="Enjoy something to eat while you fly"
242242
illustration={<Illustration name="Meal" size="small" />}
@@ -340,7 +340,7 @@ export const WithForm: Story = {
340340

341341
return (
342342
<Container>
343-
<Modal triggerRef={triggerRef} onClose={onClose} fixedFooter>
343+
<Modal triggerRef={triggerRef} onClose={onClose} labelClose="Close" fixedFooter>
344344
<ModalHeader title="Refund" description="Reservation number: 123456789" />
345345
<ModalSection>
346346
<Stack>
@@ -424,6 +424,7 @@ export const WithItinerary: Story = {
424424
triggerRef={triggerRef}
425425
ariaLabel="Itinerary from Prague to Frankfurt"
426426
onClose={onClose}
427+
labelClose="Close"
427428
>
428429
<ModalSection>
429430
<Itinerary>
@@ -607,7 +608,7 @@ export const Rtl: Story = {
607608
return (
608609
<Container>
609610
<RenderInRtl>
610-
<Modal triggerRef={triggerRef} onClose={onClose}>
611+
<Modal triggerRef={triggerRef} onClose={onClose} labelClose="Close">
611612
<ModalHeader
612613
title="The title of the ModalHeader"
613614
illustration={<Illustration name="Accommodation" size="small" />}

packages/orbit-components/src/Modal/README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ Table below contains all types of the props available in the Modal component.
3838
| hasCloseButton | `boolean` | `true` | Defines whether the Modal displays a close button. If you disable this, we recommend adding some kind of an alternative. |
3939
| disableAnimation | `boolean` | `false` | Defines whether the Modal performs the slide in animation on mobile. If you want to improve your [CLS](https://web.dev/cls/) score, you might want to set this to `true`. |
4040
| mobileHeader | `boolean` | `true` | If `false` the ModalHeader will not have MobileHeader and CloseContainer. |
41-
| labelClose | `string` | `Close` | The label for the close button. |
41+
| labelClose | `string` | | The label for the close button. It is required all the time, unless `hasCloseButton` is explicitly set to `false`. |
4242
| onScroll | `event => void \| Promise` | | Function for handling `onScroll` event. [See Functional specs](#functional-specs). |
4343
| ariaLabelledby | `string` | | The `aria-labelledby` attribute of the Modal. It should be used if `title` is not defined on the ModalHeader. |
4444
| ariaDescribedby | `string` | | The `aria-describedby` attribute of the Modal. It should be used if `description` is not defined on the ModalHeader. |

packages/orbit-components/src/Modal/__tests__/index.test.tsx

+9-9
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ const mockUseMediaQuery = useMediaQuery;
1818
describe("Modal", () => {
1919
it("should have expected DOM output", () => {
2020
render(
21-
<Modal dataTest="container">
21+
<Modal dataTest="container" labelClose="Close">
2222
<ModalHeader
2323
dataTest="header"
2424
title="modal title"
@@ -52,7 +52,7 @@ describe("Modal", () => {
5252
jest.useFakeTimers();
5353

5454
render(
55-
<Modal>
55+
<Modal labelClose="Close">
5656
<input />
5757
</Modal>,
5858
);
@@ -66,7 +66,7 @@ describe("Modal", () => {
6666
it("should expose ref scrolling API", () => {
6767
const ref = React.createRef<React.ElementRef<typeof Modal>>();
6868
render(
69-
<Modal ref={ref} dataTest="test">
69+
<Modal ref={ref} dataTest="test" labelClose="Close">
7070
content
7171
</Modal>,
7272
);
@@ -80,7 +80,7 @@ describe("Modal", () => {
8080
const onScroll = jest.fn();
8181

8282
render(
83-
<Modal onScroll={onScroll}>
83+
<Modal onScroll={onScroll} labelClose="Close">
8484
<ModalSection>
8585
<div style={{ height: 500 }}>kek</div>
8686
</ModalSection>
@@ -101,15 +101,15 @@ describe("Modal", () => {
101101
// @ts-expect-error jest
102102
mockUseMediaQuery.mockImplementation(() => ({ isLargeMobile: true }));
103103
const { rerender } = render(
104-
<Modal ref={modalRef} scrollingElementRef={scrollingElementRef}>
104+
<Modal ref={modalRef} scrollingElementRef={scrollingElementRef} labelClose="Close">
105105
content
106106
</Modal>,
107107
);
108108
expect(scrollingElement.current).toBe(modalRef.current?.modalBody.current);
109109
// @ts-expect-error jest
110110
mockUseMediaQuery.mockImplementation(() => ({ isLargeMobile: false }));
111111
rerender(
112-
<Modal ref={modalRef} scrollingElementRef={scrollingElementRef}>
112+
<Modal ref={modalRef} scrollingElementRef={scrollingElementRef} labelClose="Close">
113113
content
114114
</Modal>,
115115
);
@@ -123,7 +123,7 @@ describe("Modal", () => {
123123
const onClose = jest.fn();
124124

125125
render(
126-
<Modal hasCloseButton onClose={onClose}>
126+
<Modal hasCloseButton onClose={onClose} labelClose="Close">
127127
content
128128
</Modal>,
129129
);
@@ -137,13 +137,13 @@ describe("Modal", () => {
137137

138138
it("should have fixed header background overlay when needed", () => {
139139
const { rerender } = render(
140-
<Modal hasCloseButton onClose={() => {}}>
140+
<Modal onClose={() => {}} labelClose="Close">
141141
content
142142
</Modal>,
143143
);
144144
screen.getByTestId("CloseContainer");
145145
rerender(
146-
<Modal>
146+
<Modal labelClose="Close">
147147
<ModalHeader title="title" />
148148
</Modal>,
149149
);

packages/orbit-components/src/Modal/__typetests__/index.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export default function Test() {
1818
});
1919
}, []);
2020
return (
21-
<Modal ref={modalRef} scrollingElementRef={scrollingElementRef}>
21+
<Modal ref={modalRef} scrollingElementRef={scrollingElementRef} labelClose="Close">
2222
content
2323
</Modal>
2424
);

packages/orbit-components/src/Modal/types.d.ts

+31-22
Original file line numberDiff line numberDiff line change
@@ -7,28 +7,37 @@ import type * as Common from "../common/types";
77

88
type Size = "extraSmall" | "small" | "normal" | "large" | "extraLarge";
99

10-
export interface Props extends Common.Globals {
11-
readonly size?: Size;
12-
readonly children: React.ReactNode;
13-
readonly triggerRef?: React.RefObject<HTMLElement>;
14-
readonly lockScrolling?: boolean;
15-
readonly scrollingElementRef?: React.Ref<HTMLElement>;
16-
readonly onClose?: Common.Event<
17-
| React.KeyboardEvent<HTMLDivElement>
18-
| React.SyntheticEvent<HTMLButtonElement | HTMLDivElement | HTMLAnchorElement>
19-
>;
20-
readonly fixedFooter?: boolean;
21-
readonly onScroll?: Common.Event<React.UIEvent<HTMLDivElement>>;
22-
readonly mobileHeader?: boolean;
23-
readonly isMobileFullPage?: boolean;
24-
readonly preventOverlayClose?: boolean;
25-
readonly hasCloseButton?: boolean;
26-
readonly disableAnimation?: boolean;
27-
readonly labelClose?: string;
28-
readonly ariaLabel?: string;
29-
readonly ariaLabelledby?: string;
30-
readonly ariaDescribedby?: string;
31-
}
10+
export type closable =
11+
| {
12+
readonly hasCloseButton?: true;
13+
readonly labelClose: string;
14+
}
15+
| {
16+
readonly hasCloseButton: false;
17+
readonly labelClose?: string;
18+
};
19+
20+
export type Props = Common.Globals &
21+
closable & {
22+
readonly size?: Size;
23+
readonly children: React.ReactNode;
24+
readonly triggerRef?: React.RefObject<HTMLElement>;
25+
readonly lockScrolling?: boolean;
26+
readonly scrollingElementRef?: React.Ref<HTMLElement>;
27+
readonly onClose?: Common.Event<
28+
| React.KeyboardEvent<HTMLDivElement>
29+
| React.SyntheticEvent<HTMLButtonElement | HTMLDivElement | HTMLAnchorElement>
30+
>;
31+
readonly fixedFooter?: boolean;
32+
readonly onScroll?: Common.Event<React.UIEvent<HTMLDivElement>>;
33+
readonly mobileHeader?: boolean;
34+
readonly isMobileFullPage?: boolean;
35+
readonly preventOverlayClose?: boolean;
36+
readonly disableAnimation?: boolean;
37+
readonly ariaLabel?: string;
38+
readonly ariaLabelledby?: string;
39+
readonly ariaDescribedby?: string;
40+
};
3241

3342
export interface Instance {
3443
getScrollPosition: () => number | null;

0 commit comments

Comments
 (0)