uikit-react-public 0.39.0 → 0.40.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/Accordion/Accordion.Heading.d.ts +2 -1
- package/dist/components/Accordion/Accordion.d.ts +8 -2
- package/dist/components/Accordion/Accordion.stories.d.ts +2 -2
- package/dist/components/Alert/Alert.d.ts +2 -2
- package/dist/components/Alert/Alert.stories.d.ts +2 -2
- package/dist/components/Alert/AlertMessage.d.ts +1 -1
- package/dist/components/Alert/AlertTitle.d.ts +1 -1
- package/dist/components/ErrorDialog/ErrorDialog.d.ts +26 -0
- package/dist/components/ErrorDialog/ErrorDialog.stories.d.ts +14 -0
- package/dist/components/ErrorDialog/__tests__/ErrorDialog.test.d.ts +1 -0
- package/dist/components/ErrorDialog/index.d.ts +2 -0
- package/dist/components/Field/HelperText.d.ts +1 -1
- package/dist/components/index.d.ts +2 -0
- package/dist/index.js +6648 -6322
- package/lib/components/Accordion/Accordion.Heading.tsx +24 -8
- package/lib/components/Accordion/Accordion.Panel.tsx +7 -4
- package/lib/components/Accordion/Accordion.stories.tsx +8 -8
- package/lib/components/Accordion/Accordion.tsx +17 -23
- package/lib/components/Accordion/__tests__/__snapshots__/Accordion.test.tsx.snap +14 -14
- package/lib/components/Alert/Alert.tsx +0 -1
- package/lib/components/Alert/AlertMessage.tsx +10 -14
- package/lib/components/Alert/AlertTitle.tsx +11 -8
- package/lib/components/Alert/__tests__/__snapshots__/Alert.test.tsx.snap +29 -14
- package/lib/components/AppHeader/AppHeader.tsx +0 -1
- package/lib/components/AppHeader/__tests__/__snapshots__/AppHeader.test.tsx.snap +1 -1
- package/lib/components/AppMenu/AppMenu.tsx +2 -9
- package/lib/components/AppMenu/AppMenuItem.tsx +6 -2
- package/lib/components/AppMenu/__tests__/__snapshots__/AppMenu.test.tsx.snap +6 -6
- package/lib/components/Breadcrumbs/Breadcrumbs.tsx +0 -4
- package/lib/components/Breadcrumbs/__tests__/__snapshots__/Breadcrumbs.test.tsx.snap +10 -10
- package/lib/components/Calendar/__tests__/__snapshots__/Calendar.test.tsx.snap +85 -85
- package/lib/components/Calendar/subcomponents/Day.tsx +7 -3
- package/lib/components/Datepicker/__tests__/__snapshots__/Datepicker.test.tsx.snap +2 -2
- package/lib/components/Datepicker/subcomponents/DatepickerInput.tsx +6 -3
- package/lib/components/Dialog/BaseDialog.tsx +6 -2
- package/lib/components/ErrorDialog/ErrorDialog.stories.tsx +85 -0
- package/lib/components/ErrorDialog/ErrorDialog.tsx +433 -0
- package/lib/components/ErrorDialog/__tests__/ErrorDialog.test.tsx +145 -0
- package/lib/components/ErrorDialog/index.tsx +2 -0
- package/lib/components/Field/HelperText.tsx +9 -10
- package/lib/components/Footer/__tests__/__snapshots__/Footer.test.tsx.snap +25 -25
- package/lib/components/FooterNew/__tests__/__snapshots__/Footer.test.tsx.snap +28 -28
- package/lib/components/Header/Header.tsx +6 -3
- package/lib/components/Header/__tests__/__snapshots__/Header.test.tsx.snap +4 -4
- package/lib/components/HeaderNew/Header.tsx +0 -1
- package/lib/components/HeaderNew/__tests__/__snapshots__/Header.test.tsx.snap +2 -2
- package/lib/components/Input/Input.tsx +6 -3
- package/lib/components/Input/__tests__/__snapshots__/Input.test.tsx.snap +4 -4
- package/lib/components/Label/Label.tsx +7 -7
- package/lib/components/Label/__tests__/__snapshots__/Label.test.tsx.snap +7 -7
- package/lib/components/Link/BaseLink.tsx +8 -16
- package/lib/components/Link/__tests__/__snapshots__/link.test.tsx.snap +2 -2
- package/lib/components/Menu/Menu.tsx +0 -4
- package/lib/components/Menu/MenuItem.tsx +7 -5
- package/lib/components/Search/__tests__/__snapshots__/Search.test.tsx.snap +2 -2
- package/lib/components/Select/__tests__/__snapshots__/Select.test.tsx.snap +2 -2
- package/lib/components/Select/subcomponents/CustomOption.tsx +6 -3
- package/lib/components/Select/subcomponents/CustomSelect.tsx +6 -2
- package/lib/components/Select/subcomponents/NativeSelect.tsx +6 -3
- package/lib/components/Select/subcomponents/VisibleField.tsx +6 -1
- package/lib/components/Sidebar/Sidebar.tsx +0 -1
- package/lib/components/Sidebar/__tests__/__snapshots__/Sidebar.test.tsx.snap +2 -2
- package/lib/components/StandaloneLink/__tests__/__snapshots__/StandaloneLink.test.tsx.snap +2 -2
- package/lib/components/Textarea/Textarea.tsx +6 -3
- package/lib/components/Textarea/__tests__/__snapshots__/Textarea.test.tsx.snap +4 -4
- package/lib/components/Timepicker/Timepicker.tsx +6 -2
- package/lib/components/Timepicker/__tests__/__snapshots__/Timepicker.test.tsx.snap +2 -2
- package/lib/components/WeekPicker/subcomponents/DatepickerInput.tsx +6 -3
- package/lib/components/index.ts +3 -0
- package/package.json +1 -1
|
@@ -25,6 +25,7 @@ const Day = ({
|
|
|
25
25
|
events = [],
|
|
26
26
|
}: DayProps) => {
|
|
27
27
|
const [theme] = useTheme();
|
|
28
|
+
const { md, mdSemibold } = theme.typography.body;
|
|
28
29
|
|
|
29
30
|
// More than 3 dots displayed breaks the layout
|
|
30
31
|
const displayedEvents = events.slice(0, 3);
|
|
@@ -79,9 +80,12 @@ const Day = ({
|
|
|
79
80
|
`;
|
|
80
81
|
|
|
81
82
|
const foregroundStyle = css`
|
|
82
|
-
font-family: ${
|
|
83
|
+
font-family: ${md.fontFamily};
|
|
84
|
+
font-feature-settings: ${md.fontSettings};
|
|
83
85
|
user-select: none;
|
|
84
|
-
font-size:
|
|
86
|
+
font-size: ${md.fontSize}px;
|
|
87
|
+
font-weight: ${md.fontWeight};
|
|
88
|
+
line-height: ${md.lineHeight}%;
|
|
85
89
|
|
|
86
90
|
${
|
|
87
91
|
!isInCurrentMonth &&
|
|
@@ -92,7 +96,7 @@ const Day = ({
|
|
|
92
96
|
${
|
|
93
97
|
isToday &&
|
|
94
98
|
css`
|
|
95
|
-
font-weight:
|
|
99
|
+
font-weight: ${mdSemibold.fontWeight};
|
|
96
100
|
`
|
|
97
101
|
}
|
|
98
102
|
${
|
|
@@ -10,7 +10,7 @@ exports[`Datepicker > Snapshot: no date provided 1`] = `
|
|
|
10
10
|
data-testid="ucl-uikit-datepicker__visible-field"
|
|
11
11
|
>
|
|
12
12
|
<input
|
|
13
|
-
class="ucl-uikit-datepicker__input css-
|
|
13
|
+
class="ucl-uikit-datepicker__input css-19gsrgi"
|
|
14
14
|
data-testid="ucl-uikit-datepicker__input"
|
|
15
15
|
inputmode="numeric"
|
|
16
16
|
placeholder="DD/MM/YYYY"
|
|
@@ -65,7 +65,7 @@ exports[`Datepicker > Snapshot: with date provided 1`] = `
|
|
|
65
65
|
data-testid="ucl-uikit-datepicker__visible-field"
|
|
66
66
|
>
|
|
67
67
|
<input
|
|
68
|
-
class="ucl-uikit-datepicker__input css-
|
|
68
|
+
class="ucl-uikit-datepicker__input css-19gsrgi"
|
|
69
69
|
data-testid="ucl-uikit-datepicker__input"
|
|
70
70
|
inputmode="numeric"
|
|
71
71
|
placeholder="DD/MM/YYYY"
|
|
@@ -38,6 +38,7 @@ const DatepickerInput = ({
|
|
|
38
38
|
...props
|
|
39
39
|
}: DatepickerInputProps) => {
|
|
40
40
|
const [theme] = useTheme();
|
|
41
|
+
const { md } = theme.typography.body;
|
|
41
42
|
|
|
42
43
|
const baseStyle = css`
|
|
43
44
|
width: 100%;
|
|
@@ -46,9 +47,11 @@ const DatepickerInput = ({
|
|
|
46
47
|
border: none;
|
|
47
48
|
color: ${theme.colour.text.default};
|
|
48
49
|
background-color: ${theme.colour.fill.inverse};
|
|
49
|
-
font-family: ${
|
|
50
|
-
font-
|
|
51
|
-
|
|
50
|
+
font-family: ${md.fontFamily};
|
|
51
|
+
font-feature-settings: ${md.fontSettings};
|
|
52
|
+
font-size: ${md.fontSize}px;
|
|
53
|
+
font-weight: ${md.fontWeight};
|
|
54
|
+
line-height: ${md.lineHeight}%;
|
|
52
55
|
caret-color: ${theme.colour.text.default};
|
|
53
56
|
|
|
54
57
|
&:focus {
|
|
@@ -63,6 +63,7 @@ const BaseDialog = forwardRef<HTMLDialogElement, BaseDialogProps>(
|
|
|
63
63
|
}[size];
|
|
64
64
|
|
|
65
65
|
const [theme] = useTheme();
|
|
66
|
+
const { md } = theme.typography.body;
|
|
66
67
|
|
|
67
68
|
const dialogRef = useRef<HTMLDialogElement>(null);
|
|
68
69
|
const previousActiveElement = useRef<HTMLElement | null>(null);
|
|
@@ -179,8 +180,11 @@ const BaseDialog = forwardRef<HTMLDialogElement, BaseDialogProps>(
|
|
|
179
180
|
border: none;
|
|
180
181
|
background: ${theme.colour.surface.default};
|
|
181
182
|
color: ${theme.colour.text.default};
|
|
182
|
-
font-family: ${
|
|
183
|
-
font-
|
|
183
|
+
font-family: ${md.fontFamily};
|
|
184
|
+
font-feature-settings: ${md.fontSettings};
|
|
185
|
+
font-size: ${md.fontSize}px;
|
|
186
|
+
font-weight: ${md.fontWeight};
|
|
187
|
+
line-height: ${md.lineHeight}%;
|
|
184
188
|
width: 100vw;
|
|
185
189
|
height: 100vh;
|
|
186
190
|
margin: auto;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
|
+
import ErrorDialog from './ErrorDialog';
|
|
3
|
+
|
|
4
|
+
const meta = {
|
|
5
|
+
title: 'Components/ErrorDialog',
|
|
6
|
+
component: ErrorDialog,
|
|
7
|
+
parameters: {
|
|
8
|
+
docs: {
|
|
9
|
+
story: 'Default',
|
|
10
|
+
},
|
|
11
|
+
},
|
|
12
|
+
} satisfies Meta<typeof ErrorDialog>;
|
|
13
|
+
|
|
14
|
+
export default meta;
|
|
15
|
+
|
|
16
|
+
type Story = StoryObj<typeof meta>;
|
|
17
|
+
|
|
18
|
+
export const Default: Story = {
|
|
19
|
+
args: {
|
|
20
|
+
open: true,
|
|
21
|
+
modal: true,
|
|
22
|
+
heading: 'Something went wrong',
|
|
23
|
+
errorCode: '1601',
|
|
24
|
+
referenceCode: 'UCLCID-20e935c3',
|
|
25
|
+
children:
|
|
26
|
+
'We couldn’t complete your request right now. Please try again in a moment.',
|
|
27
|
+
details: [
|
|
28
|
+
{
|
|
29
|
+
label: 'HTTP Status Code',
|
|
30
|
+
value: 'HTTP status code: 404 (Not Found)',
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
label: 'Online Status',
|
|
34
|
+
value: 'Network connection: online',
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
label: 'Username',
|
|
38
|
+
value: 'username: j.bloggs',
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
label: 'Login Time',
|
|
42
|
+
value: 'login time: Fri Jan 16 2026 10:01:43 GMT+0000',
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
primaryButton: 'Primary',
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export const LongContent: Story = {
|
|
50
|
+
args: {
|
|
51
|
+
open: true,
|
|
52
|
+
modal: true,
|
|
53
|
+
heading: 'The file could not be uploaded',
|
|
54
|
+
errorCode: '1601',
|
|
55
|
+
referenceCode: 'UCLCID-20e935c3',
|
|
56
|
+
details: [
|
|
57
|
+
{
|
|
58
|
+
label: 'HTTP Status Code',
|
|
59
|
+
value:
|
|
60
|
+
'HTTP status code: 404 (Not Found)\nAPI request method: GET\nNetwork connection: online',
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
label: 'User Details',
|
|
64
|
+
value:
|
|
65
|
+
'username: j.bloggs\nlogin time: Fri Jan 16 2026 10:01:43 GMT+0000\nonline status: online',
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
label: 'Request Details',
|
|
69
|
+
value:
|
|
70
|
+
'path: /rooms/201/312\nrequest URL: https://timetable.mesh.ucl.ac.uk/online-timetable-api-proxy/v0.1/location',
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
label: 'Browser Details',
|
|
74
|
+
value: 'browser: Chrome\nOS: macOS\ndevice type: desktop',
|
|
75
|
+
},
|
|
76
|
+
],
|
|
77
|
+
children: Array(20)
|
|
78
|
+
.fill(
|
|
79
|
+
'The upload service did not respond before the request timed out. Check the file and try again.'
|
|
80
|
+
)
|
|
81
|
+
.join(' '),
|
|
82
|
+
primaryButton: 'Primary',
|
|
83
|
+
footerDivider: true,
|
|
84
|
+
},
|
|
85
|
+
};
|
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
import { ReactNode, useCallback, useId } from 'react';
|
|
2
|
+
import { css, cx } from '@emotion/css';
|
|
3
|
+
import useTheme from '../../theme/useTheme';
|
|
4
|
+
import BaseDialog, { BaseDialogProps } from '../Dialog/BaseDialog';
|
|
5
|
+
import Heading from '../Heading';
|
|
6
|
+
import Paragraph from '../Paragraph';
|
|
7
|
+
import Text from '../Text';
|
|
8
|
+
import Button, { ButtonProps } from '../Button';
|
|
9
|
+
import Icon from '../Icon';
|
|
10
|
+
import IconButton, { IconButtonProps } from '../IconButton';
|
|
11
|
+
import Accordion from '../Accordion';
|
|
12
|
+
|
|
13
|
+
export const NAME = 'ucl-uikit-error-dialog';
|
|
14
|
+
|
|
15
|
+
export interface ErrorDialogDetail {
|
|
16
|
+
label: string;
|
|
17
|
+
value: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface ErrorDialogProps extends BaseDialogProps {
|
|
21
|
+
heading: ReactNode;
|
|
22
|
+
errorCode?: ReactNode;
|
|
23
|
+
referenceCode?: ReactNode;
|
|
24
|
+
details?: ErrorDialogDetail[];
|
|
25
|
+
primaryButton?: ReactNode;
|
|
26
|
+
secondaryButton?: ReactNode;
|
|
27
|
+
primaryButtonProps?: ButtonProps;
|
|
28
|
+
secondaryButtonProps?: ButtonProps;
|
|
29
|
+
closeButtonProps?: IconButtonProps;
|
|
30
|
+
closeButtonAriaLabel?: string;
|
|
31
|
+
footerDivider?: boolean;
|
|
32
|
+
onAction?: () => void;
|
|
33
|
+
onSecondaryAction?: () => void;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const ErrorDialog = ({
|
|
37
|
+
heading,
|
|
38
|
+
errorCode,
|
|
39
|
+
referenceCode,
|
|
40
|
+
details = [],
|
|
41
|
+
primaryButton = 'Close',
|
|
42
|
+
secondaryButton,
|
|
43
|
+
primaryButtonProps,
|
|
44
|
+
secondaryButtonProps,
|
|
45
|
+
closeButtonProps,
|
|
46
|
+
closeButtonAriaLabel = 'Close error dialog',
|
|
47
|
+
footerDivider = false,
|
|
48
|
+
onAction,
|
|
49
|
+
onSecondaryAction,
|
|
50
|
+
onClose,
|
|
51
|
+
size = 'medium',
|
|
52
|
+
modal = true,
|
|
53
|
+
role = 'alertdialog',
|
|
54
|
+
testId = NAME,
|
|
55
|
+
className,
|
|
56
|
+
children,
|
|
57
|
+
...props
|
|
58
|
+
}: ErrorDialogProps) => {
|
|
59
|
+
const [theme] = useTheme();
|
|
60
|
+
const dialogHeaderId = useId();
|
|
61
|
+
const dialogBodyId = useId();
|
|
62
|
+
const hasBody = children !== undefined && children !== null;
|
|
63
|
+
const hasErrorCode = errorCode !== undefined && errorCode !== null;
|
|
64
|
+
const hasReferenceCode =
|
|
65
|
+
referenceCode !== undefined && referenceCode !== null;
|
|
66
|
+
const hasFooterMetadata = hasErrorCode || hasReferenceCode;
|
|
67
|
+
const hasDetails = details.length > 0;
|
|
68
|
+
const hasPrimaryButton =
|
|
69
|
+
primaryButton !== undefined && primaryButton !== null;
|
|
70
|
+
const hasSecondaryButton =
|
|
71
|
+
secondaryButton !== undefined && secondaryButton !== null;
|
|
72
|
+
const hasFooterActions = hasPrimaryButton || hasSecondaryButton;
|
|
73
|
+
|
|
74
|
+
const handleCloseButtonClick = useCallback(
|
|
75
|
+
(ev: React.MouseEvent<HTMLButtonElement>) => {
|
|
76
|
+
closeButtonProps?.onClick?.(ev);
|
|
77
|
+
|
|
78
|
+
if (!ev.defaultPrevented) {
|
|
79
|
+
onClose?.(ev);
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
[closeButtonProps, onClose]
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
const handlePrimaryButtonClick = useCallback(
|
|
86
|
+
(ev: React.MouseEvent<HTMLButtonElement>) => {
|
|
87
|
+
primaryButtonProps?.onClick?.(ev);
|
|
88
|
+
|
|
89
|
+
if (ev.defaultPrevented) return;
|
|
90
|
+
|
|
91
|
+
if (onAction) {
|
|
92
|
+
onAction();
|
|
93
|
+
} else {
|
|
94
|
+
onClose?.(ev);
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
[onAction, onClose, primaryButtonProps]
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
const handleSecondaryButtonClick = useCallback(
|
|
101
|
+
(ev: React.MouseEvent<HTMLButtonElement>) => {
|
|
102
|
+
secondaryButtonProps?.onClick?.(ev);
|
|
103
|
+
|
|
104
|
+
if (ev.defaultPrevented) return;
|
|
105
|
+
|
|
106
|
+
if (onSecondaryAction) {
|
|
107
|
+
onSecondaryAction();
|
|
108
|
+
} else {
|
|
109
|
+
onClose?.(ev);
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
[onClose, onSecondaryAction, secondaryButtonProps]
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
const baseStyle = css`
|
|
116
|
+
box-shadow: ${theme.boxShadow.x2y4};
|
|
117
|
+
border-top: ${theme.padding.p4} solid ${theme.colour.border.critical};
|
|
118
|
+
display: flex;
|
|
119
|
+
flex-direction: column;
|
|
120
|
+
overflow: hidden;
|
|
121
|
+
`;
|
|
122
|
+
|
|
123
|
+
const style = cx(NAME, baseStyle, className);
|
|
124
|
+
|
|
125
|
+
const headerBaseStyle = css`
|
|
126
|
+
flex: 0 0 auto;
|
|
127
|
+
display: flex;
|
|
128
|
+
align-items: flex-start;
|
|
129
|
+
gap: ${theme.padding.p8};
|
|
130
|
+
padding: ${theme.padding.p24} ${theme.padding.p24} 0;
|
|
131
|
+
|
|
132
|
+
@media (min-width: ${theme.breakpoints.tablet}px) {
|
|
133
|
+
padding: ${theme.padding.p32} ${theme.padding.p32} 0;
|
|
134
|
+
}
|
|
135
|
+
`;
|
|
136
|
+
|
|
137
|
+
const headerStyle = cx(`${NAME}__header`, headerBaseStyle);
|
|
138
|
+
|
|
139
|
+
const headerContentBaseStyle = css`
|
|
140
|
+
flex: 1 1 auto;
|
|
141
|
+
min-width: 0;
|
|
142
|
+
display: flex;
|
|
143
|
+
flex-direction: column;
|
|
144
|
+
align-items: flex-start;
|
|
145
|
+
gap: ${theme.padding.p16};
|
|
146
|
+
`;
|
|
147
|
+
|
|
148
|
+
const headerContentStyle = cx(
|
|
149
|
+
`${NAME}__header-content`,
|
|
150
|
+
headerContentBaseStyle
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
const closeButtonBaseStyle = css`
|
|
154
|
+
flex: 0 0 auto;
|
|
155
|
+
color: ${theme.colour.icon.secondary};
|
|
156
|
+
`;
|
|
157
|
+
|
|
158
|
+
const closeButtonStyle = cx(
|
|
159
|
+
`${NAME}__close-button`,
|
|
160
|
+
closeButtonBaseStyle,
|
|
161
|
+
closeButtonProps?.className
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
const iconBaseStyle = css`
|
|
165
|
+
color: ${theme.colour.icon.critical};
|
|
166
|
+
flex: 0 0 auto;
|
|
167
|
+
`;
|
|
168
|
+
|
|
169
|
+
const iconStyle = cx(`${NAME}__icon`, iconBaseStyle);
|
|
170
|
+
|
|
171
|
+
const bodyBaseStyle = css`
|
|
172
|
+
flex: 1 1 auto;
|
|
173
|
+
min-height: 0;
|
|
174
|
+
overflow-y: auto;
|
|
175
|
+
padding: ${theme.padding.p16} ${theme.padding.p24};
|
|
176
|
+
|
|
177
|
+
@media (min-width: ${theme.breakpoints.tablet}px) {
|
|
178
|
+
padding: ${theme.padding.p16} ${theme.padding.p32};
|
|
179
|
+
}
|
|
180
|
+
`;
|
|
181
|
+
|
|
182
|
+
const bodyStyle = cx(`${NAME}__body`, bodyBaseStyle);
|
|
183
|
+
|
|
184
|
+
const bodyContentBaseStyle = css`
|
|
185
|
+
display: flex;
|
|
186
|
+
flex-direction: column;
|
|
187
|
+
gap: ${theme.padding.p8};
|
|
188
|
+
`;
|
|
189
|
+
|
|
190
|
+
const bodyContentStyle = cx(`${NAME}__body-content`, bodyContentBaseStyle);
|
|
191
|
+
|
|
192
|
+
const detailsBaseStyle = css`
|
|
193
|
+
display: flex;
|
|
194
|
+
flex-direction: column;
|
|
195
|
+
gap: ${theme.padding.p8};
|
|
196
|
+
margin-top: ${theme.margin.m16};
|
|
197
|
+
`;
|
|
198
|
+
|
|
199
|
+
const detailsStyle = cx(`${NAME}__details`, detailsBaseStyle);
|
|
200
|
+
|
|
201
|
+
const detailAccordionBaseStyle = css`
|
|
202
|
+
width: 100%;
|
|
203
|
+
max-width: none;
|
|
204
|
+
`;
|
|
205
|
+
|
|
206
|
+
const detailAccordionStyle = cx(`${NAME}__detail`, detailAccordionBaseStyle);
|
|
207
|
+
|
|
208
|
+
const detailValueBaseStyle = css`
|
|
209
|
+
white-space: pre-line;
|
|
210
|
+
`;
|
|
211
|
+
|
|
212
|
+
const detailValueStyle = cx(`${NAME}__detail-value`, detailValueBaseStyle);
|
|
213
|
+
|
|
214
|
+
const errorCodeBaseStyle = css`
|
|
215
|
+
color: ${theme.colour.text.default};
|
|
216
|
+
opacity: 0.9;
|
|
217
|
+
`;
|
|
218
|
+
|
|
219
|
+
const errorCodeStyle = cx(`${NAME}__error-code`, errorCodeBaseStyle);
|
|
220
|
+
|
|
221
|
+
const referenceCodeBaseStyle = css`
|
|
222
|
+
color: ${theme.colour.text.default};
|
|
223
|
+
opacity: 0.9;
|
|
224
|
+
`;
|
|
225
|
+
|
|
226
|
+
const referenceCodeStyle = cx(
|
|
227
|
+
`${NAME}__reference-code`,
|
|
228
|
+
referenceCodeBaseStyle
|
|
229
|
+
);
|
|
230
|
+
|
|
231
|
+
const footerBaseStyle = css`
|
|
232
|
+
flex: 0 0 auto;
|
|
233
|
+
display: flex;
|
|
234
|
+
flex-direction: column-reverse;
|
|
235
|
+
gap: ${theme.padding.p12};
|
|
236
|
+
padding: ${theme.padding.p16} ${theme.padding.p24} ${theme.padding.p24};
|
|
237
|
+
|
|
238
|
+
@media (min-width: ${theme.breakpoints.tablet}px) {
|
|
239
|
+
flex-direction: row;
|
|
240
|
+
justify-content: space-between;
|
|
241
|
+
align-items: center;
|
|
242
|
+
padding: ${theme.padding.p16} ${theme.padding.p32} ${theme.padding.p32};
|
|
243
|
+
}
|
|
244
|
+
`;
|
|
245
|
+
|
|
246
|
+
const footerDividerStyle = css`
|
|
247
|
+
border-top: ${theme.border.b1} solid ${theme.colour.border.default};
|
|
248
|
+
`;
|
|
249
|
+
|
|
250
|
+
const footerStyle = cx(
|
|
251
|
+
`${NAME}__footer`,
|
|
252
|
+
footerBaseStyle,
|
|
253
|
+
footerDivider && footerDividerStyle
|
|
254
|
+
);
|
|
255
|
+
|
|
256
|
+
const footerMetadataBaseStyle = css`
|
|
257
|
+
display: flex;
|
|
258
|
+
flex-direction: column;
|
|
259
|
+
gap: ${theme.padding.p4};
|
|
260
|
+
`;
|
|
261
|
+
|
|
262
|
+
const footerMetadataStyle = cx(
|
|
263
|
+
`${NAME}__footer-metadata`,
|
|
264
|
+
footerMetadataBaseStyle
|
|
265
|
+
);
|
|
266
|
+
|
|
267
|
+
const footerActionsBaseStyle = css`
|
|
268
|
+
display: flex;
|
|
269
|
+
flex-direction: column-reverse;
|
|
270
|
+
gap: ${theme.padding.p12};
|
|
271
|
+
|
|
272
|
+
@media (min-width: ${theme.breakpoints.tablet}px) {
|
|
273
|
+
flex-direction: row;
|
|
274
|
+
justify-content: flex-end;
|
|
275
|
+
align-items: center;
|
|
276
|
+
}
|
|
277
|
+
`;
|
|
278
|
+
|
|
279
|
+
const footerActionsStyle = cx(
|
|
280
|
+
`${NAME}__footer-actions`,
|
|
281
|
+
footerActionsBaseStyle
|
|
282
|
+
);
|
|
283
|
+
|
|
284
|
+
const buttonBaseStyle = css`
|
|
285
|
+
width: 100%;
|
|
286
|
+
|
|
287
|
+
@media (min-width: ${theme.breakpoints.tablet}px) {
|
|
288
|
+
width: auto;
|
|
289
|
+
}
|
|
290
|
+
`;
|
|
291
|
+
|
|
292
|
+
const primaryButtonStyle = cx(
|
|
293
|
+
`${NAME}__primary-button`,
|
|
294
|
+
buttonBaseStyle,
|
|
295
|
+
primaryButtonProps?.className
|
|
296
|
+
);
|
|
297
|
+
|
|
298
|
+
const secondaryButtonStyle = cx(
|
|
299
|
+
`${NAME}__secondary-button`,
|
|
300
|
+
buttonBaseStyle,
|
|
301
|
+
secondaryButtonProps?.className
|
|
302
|
+
);
|
|
303
|
+
|
|
304
|
+
return (
|
|
305
|
+
<BaseDialog
|
|
306
|
+
size={size}
|
|
307
|
+
modal={modal}
|
|
308
|
+
role={role}
|
|
309
|
+
aria-labelledby={dialogHeaderId}
|
|
310
|
+
aria-describedby={dialogBodyId}
|
|
311
|
+
onClose={onClose}
|
|
312
|
+
testId={testId}
|
|
313
|
+
className={style}
|
|
314
|
+
skipCloseOnInitialFocus={true}
|
|
315
|
+
{...props}
|
|
316
|
+
>
|
|
317
|
+
<header className={headerStyle}>
|
|
318
|
+
<div className={headerContentStyle}>
|
|
319
|
+
<Icon.AlertCircle
|
|
320
|
+
size={32}
|
|
321
|
+
className={iconStyle}
|
|
322
|
+
role='presentation'
|
|
323
|
+
aria-hidden='true'
|
|
324
|
+
/>
|
|
325
|
+
<Heading
|
|
326
|
+
id={dialogHeaderId}
|
|
327
|
+
level='lg'
|
|
328
|
+
as='h3'
|
|
329
|
+
noMargins
|
|
330
|
+
>
|
|
331
|
+
{heading}
|
|
332
|
+
</Heading>
|
|
333
|
+
</div>
|
|
334
|
+
<IconButton
|
|
335
|
+
aria-label={closeButtonAriaLabel}
|
|
336
|
+
{...closeButtonProps}
|
|
337
|
+
className={closeButtonStyle}
|
|
338
|
+
onClick={handleCloseButtonClick}
|
|
339
|
+
>
|
|
340
|
+
<Icon.X
|
|
341
|
+
role='presentation'
|
|
342
|
+
aria-hidden='true'
|
|
343
|
+
/>
|
|
344
|
+
</IconButton>
|
|
345
|
+
</header>
|
|
346
|
+
<div
|
|
347
|
+
id={dialogBodyId}
|
|
348
|
+
className={bodyStyle}
|
|
349
|
+
role='document'
|
|
350
|
+
>
|
|
351
|
+
<div className={bodyContentStyle}>
|
|
352
|
+
{hasBody && (
|
|
353
|
+
<Paragraph
|
|
354
|
+
as='div'
|
|
355
|
+
level='md'
|
|
356
|
+
noMargins
|
|
357
|
+
>
|
|
358
|
+
{children}
|
|
359
|
+
</Paragraph>
|
|
360
|
+
)}
|
|
361
|
+
{hasDetails && (
|
|
362
|
+
<div className={detailsStyle}>
|
|
363
|
+
{details.map(({ label, value }, index) => (
|
|
364
|
+
<Accordion
|
|
365
|
+
key={`${label}-${index}`}
|
|
366
|
+
className={detailAccordionStyle}
|
|
367
|
+
>
|
|
368
|
+
<Accordion.Heading>{label}</Accordion.Heading>
|
|
369
|
+
<Accordion.Panel>
|
|
370
|
+
<span className={detailValueStyle}>{value}</span>
|
|
371
|
+
</Accordion.Panel>
|
|
372
|
+
</Accordion>
|
|
373
|
+
))}
|
|
374
|
+
</div>
|
|
375
|
+
)}
|
|
376
|
+
</div>
|
|
377
|
+
</div>
|
|
378
|
+
{(hasFooterActions || hasFooterMetadata) && (
|
|
379
|
+
<footer
|
|
380
|
+
className={footerStyle}
|
|
381
|
+
aria-label='Error dialog actions'
|
|
382
|
+
>
|
|
383
|
+
{hasFooterMetadata && (
|
|
384
|
+
<div className={footerMetadataStyle}>
|
|
385
|
+
{hasErrorCode && (
|
|
386
|
+
<Text
|
|
387
|
+
className={errorCodeStyle}
|
|
388
|
+
level='md-semibold'
|
|
389
|
+
>
|
|
390
|
+
Error code: {errorCode}
|
|
391
|
+
</Text>
|
|
392
|
+
)}
|
|
393
|
+
{hasReferenceCode && (
|
|
394
|
+
<Text
|
|
395
|
+
className={referenceCodeStyle}
|
|
396
|
+
level='md-semibold'
|
|
397
|
+
>
|
|
398
|
+
Reference code: {referenceCode}
|
|
399
|
+
</Text>
|
|
400
|
+
)}
|
|
401
|
+
</div>
|
|
402
|
+
)}
|
|
403
|
+
{hasFooterActions && (
|
|
404
|
+
<div className={footerActionsStyle}>
|
|
405
|
+
{hasSecondaryButton && (
|
|
406
|
+
<Button
|
|
407
|
+
variant='secondary-destructive'
|
|
408
|
+
{...secondaryButtonProps}
|
|
409
|
+
className={secondaryButtonStyle}
|
|
410
|
+
onClick={handleSecondaryButtonClick}
|
|
411
|
+
>
|
|
412
|
+
{secondaryButton}
|
|
413
|
+
</Button>
|
|
414
|
+
)}
|
|
415
|
+
{hasPrimaryButton && (
|
|
416
|
+
<Button
|
|
417
|
+
variant='primary-destructive'
|
|
418
|
+
{...primaryButtonProps}
|
|
419
|
+
className={primaryButtonStyle}
|
|
420
|
+
onClick={handlePrimaryButtonClick}
|
|
421
|
+
>
|
|
422
|
+
{primaryButton}
|
|
423
|
+
</Button>
|
|
424
|
+
)}
|
|
425
|
+
</div>
|
|
426
|
+
)}
|
|
427
|
+
</footer>
|
|
428
|
+
)}
|
|
429
|
+
</BaseDialog>
|
|
430
|
+
);
|
|
431
|
+
};
|
|
432
|
+
|
|
433
|
+
export default ErrorDialog;
|