@aarhus-university/au-lib-react-components 10.8.0 → 10.9.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/__tests__/jest/AUErrorComponent.test.tsx +142 -0
- package/__tests__/jest/AUNotificationComponent.test.tsx +1 -1
- package/package.json +3 -2
- package/src/components/AUErrorComponent.tsx +78 -0
- package/src/components/AUNotificationComponent.tsx +5 -5
- package/stories/AUErrorComponent.stories.tsx +117 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { render } from '@testing-library/react';
|
|
4
|
+
import { SerializedError } from '@reduxjs/toolkit';
|
|
5
|
+
import { FetchBaseQueryError } from '@reduxjs/toolkit/dist/query';
|
|
6
|
+
// import userEvent from '@testing-library/user-event';
|
|
7
|
+
import '@testing-library/jest-dom';
|
|
8
|
+
import AUErrorComponent from '../../src/components/AUErrorComponent';
|
|
9
|
+
|
|
10
|
+
const auError: AU.IError = {
|
|
11
|
+
header: 'Our own custom error',
|
|
12
|
+
message: 'With a nice header and descriptive error message',
|
|
13
|
+
status: 500,
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const fetchError1: FetchBaseQueryError = {
|
|
17
|
+
status: 'FETCH_ERROR',
|
|
18
|
+
error: 'Sum Ting Wong',
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const fetchError2: FetchBaseQueryError = {
|
|
22
|
+
status: 500,
|
|
23
|
+
data: {
|
|
24
|
+
status: 'Error',
|
|
25
|
+
message: 'Sum Ting Wong',
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const serializedError: SerializedError = {
|
|
30
|
+
code: '500',
|
|
31
|
+
message: 'Sum Ting Wong',
|
|
32
|
+
name: 'Some kind of error',
|
|
33
|
+
stack: 'Some kind of stack trace',
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
describe('<AUErrorComponent />', () => {
|
|
37
|
+
it('renders an AU custom error without a status code', () => {
|
|
38
|
+
const { container } = render(
|
|
39
|
+
<AUErrorComponent
|
|
40
|
+
error={auError}
|
|
41
|
+
/>,
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
const header = container.querySelector('.notification__header');
|
|
45
|
+
const p = container.querySelector('.notification__content p');
|
|
46
|
+
expect(header).toBeInTheDocument();
|
|
47
|
+
expect(p?.innerHTML).toBe(auError.message);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('renders an AU custom error with a status code', () => {
|
|
51
|
+
const { container } = render(
|
|
52
|
+
<AUErrorComponent
|
|
53
|
+
error={auError}
|
|
54
|
+
withStatus
|
|
55
|
+
/>,
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
const header = container.querySelector('.notification__header');
|
|
59
|
+
expect(header?.innerHTML).toBe(`${auError.status}: ${auError.header}`);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('renders a fetch error without a status code', () => {
|
|
63
|
+
const { container } = render(
|
|
64
|
+
<AUErrorComponent
|
|
65
|
+
fetchError={fetchError1}
|
|
66
|
+
/>,
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
const header = container.querySelector('.notification__header');
|
|
70
|
+
const p = container.querySelector('.notification__content p');
|
|
71
|
+
expect(header).toBe(null);
|
|
72
|
+
expect(p?.innerHTML).toBe(fetchError1.error);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('renders a fetch error with a status code', () => {
|
|
76
|
+
const { container } = render(
|
|
77
|
+
<AUErrorComponent
|
|
78
|
+
fetchError={fetchError1}
|
|
79
|
+
withStatus
|
|
80
|
+
/>,
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
const header = container.querySelector('.notification__header');
|
|
84
|
+
const p = container.querySelector('.notification__content p');
|
|
85
|
+
expect(header?.innerHTML).toBe(fetchError1.status);
|
|
86
|
+
expect(p?.innerHTML).toBe(fetchError1.error);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('renders a fetch error with stringified text without a status code', () => {
|
|
90
|
+
const { container } = render(
|
|
91
|
+
<AUErrorComponent
|
|
92
|
+
fetchError={fetchError2}
|
|
93
|
+
/>,
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
const header = container.querySelector('.notification__header');
|
|
97
|
+
const p = container.querySelector('.notification__content p');
|
|
98
|
+
expect(header).toBe(null);
|
|
99
|
+
expect(p?.innerHTML).toBe(JSON.stringify(fetchError2.data));
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('renders a fetch error with stringified text with a status code', () => {
|
|
103
|
+
const { container } = render(
|
|
104
|
+
<AUErrorComponent
|
|
105
|
+
fetchError={fetchError2}
|
|
106
|
+
withStatus
|
|
107
|
+
/>,
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
const header = container.querySelector('.notification__header');
|
|
111
|
+
const p = container.querySelector('.notification__content p');
|
|
112
|
+
expect(header?.innerHTML).toBe(`${fetchError2.status}`);
|
|
113
|
+
expect(p?.innerHTML).toBe(JSON.stringify(fetchError2.data));
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('renders a serialized error with without a status code', () => {
|
|
117
|
+
const { container } = render(
|
|
118
|
+
<AUErrorComponent
|
|
119
|
+
serializedError={serializedError}
|
|
120
|
+
/>,
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
const header = container.querySelector('.notification__header');
|
|
124
|
+
const p = container.querySelector('.notification__content p');
|
|
125
|
+
expect(header).toBe(null);
|
|
126
|
+
expect(p?.innerHTML).toBe(serializedError.message);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it('renders a serialized error with a status code', () => {
|
|
130
|
+
const { container } = render(
|
|
131
|
+
<AUErrorComponent
|
|
132
|
+
serializedError={serializedError}
|
|
133
|
+
withStatus
|
|
134
|
+
/>,
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
const header = container.querySelector('.notification__header');
|
|
138
|
+
const p = container.querySelector('.notification__content p');
|
|
139
|
+
expect(header?.innerHTML).toBe(serializedError.code);
|
|
140
|
+
expect(p?.innerHTML).toBe(serializedError.message);
|
|
141
|
+
});
|
|
142
|
+
});
|
|
@@ -6,7 +6,7 @@ import '@testing-library/jest-dom';
|
|
|
6
6
|
import AUNotificationComponent from '../../src/components/AUNotificationComponent';
|
|
7
7
|
import AUButtonComponent from '../../src/components/AUButtonComponent';
|
|
8
8
|
|
|
9
|
-
describe('<
|
|
9
|
+
describe('<AUNotificationComponent />', () => {
|
|
10
10
|
it('renders a notification without a header but with a paragraph', () => {
|
|
11
11
|
const { container } = render(
|
|
12
12
|
<AUNotificationComponent
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"sideEffects": false,
|
|
3
3
|
"name": "@aarhus-university/au-lib-react-components",
|
|
4
|
-
"version": "10.
|
|
4
|
+
"version": "10.9.0",
|
|
5
5
|
"description": "Library for shared React components for various applications on au.dk",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"test": "jest",
|
|
@@ -69,7 +69,8 @@
|
|
|
69
69
|
},
|
|
70
70
|
"dependencies": {
|
|
71
71
|
"@aarhus-university/au-designsystem-delphinus": "0.29.0",
|
|
72
|
-
"@aarhus-university/types": "^0.
|
|
72
|
+
"@aarhus-university/types": "^0.5.0",
|
|
73
|
+
"@reduxjs/toolkit": "^1.8.3",
|
|
73
74
|
"@types/google.analytics": "^0.0.42",
|
|
74
75
|
"@types/history": "^5.0.0",
|
|
75
76
|
"@types/react": "^18.0.14",
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import React, { FC } from 'react';
|
|
2
|
+
import { SerializedError } from '@reduxjs/toolkit';
|
|
3
|
+
import { FetchBaseQueryError } from '@reduxjs/toolkit/query';
|
|
4
|
+
import AUNotificationComponent from './AUNotificationComponent';
|
|
5
|
+
|
|
6
|
+
interface AUErrorComponentProps {
|
|
7
|
+
error?: AU.IError,
|
|
8
|
+
fetchError?: FetchBaseQueryError,
|
|
9
|
+
serializedError?: SerializedError,
|
|
10
|
+
withStatus?: boolean,
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const AUErrorComponent: FC<AUErrorComponentProps> = ({
|
|
14
|
+
error: auError,
|
|
15
|
+
fetchError,
|
|
16
|
+
serializedError,
|
|
17
|
+
withStatus,
|
|
18
|
+
}: AUErrorComponentProps) => {
|
|
19
|
+
if (auError) {
|
|
20
|
+
const { status, header, message } = auError;
|
|
21
|
+
return (
|
|
22
|
+
<AUNotificationComponent
|
|
23
|
+
type="warning"
|
|
24
|
+
header={`${withStatus ? `${status}: ` : ''}${header}`}
|
|
25
|
+
content={[
|
|
26
|
+
<p>{message}</p>,
|
|
27
|
+
]}
|
|
28
|
+
/>
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
if (fetchError && 'error' in fetchError) {
|
|
32
|
+
const { status, error } = fetchError;
|
|
33
|
+
return (
|
|
34
|
+
<AUNotificationComponent
|
|
35
|
+
type="warning"
|
|
36
|
+
header={withStatus ? status : undefined}
|
|
37
|
+
content={[
|
|
38
|
+
<p>{error}</p>,
|
|
39
|
+
]}
|
|
40
|
+
/>
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
if (fetchError && 'data' in fetchError) {
|
|
44
|
+
const { status, data } = fetchError;
|
|
45
|
+
return (
|
|
46
|
+
<AUNotificationComponent
|
|
47
|
+
type="warning"
|
|
48
|
+
header={withStatus ? `${status}` : undefined}
|
|
49
|
+
content={[
|
|
50
|
+
<p>{JSON.stringify(data)}</p>,
|
|
51
|
+
]}
|
|
52
|
+
/>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
if (serializedError && 'message' in serializedError) {
|
|
56
|
+
const { code, message } = serializedError;
|
|
57
|
+
return (
|
|
58
|
+
<AUNotificationComponent
|
|
59
|
+
type="warning"
|
|
60
|
+
header={withStatus ? code : undefined}
|
|
61
|
+
content={[
|
|
62
|
+
<p>{message}</p>,
|
|
63
|
+
]}
|
|
64
|
+
/>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
return null;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
AUErrorComponent.defaultProps = {
|
|
71
|
+
error: undefined,
|
|
72
|
+
fetchError: undefined,
|
|
73
|
+
serializedError: undefined,
|
|
74
|
+
withStatus: false,
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
AUErrorComponent.displayName = 'AUErrorComponent';
|
|
78
|
+
export default AUErrorComponent;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import React, { FC } from 'react';
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const AUNotificationComponent: FC<AUNotificationComponentProps> = ({
|
|
4
4
|
header,
|
|
5
5
|
content,
|
|
6
6
|
actions,
|
|
7
7
|
type,
|
|
8
8
|
classNames,
|
|
9
|
-
}:
|
|
9
|
+
}: AUNotificationComponentProps) => {
|
|
10
10
|
const typeClass = type === 'default' ? '' : `notification--${type}`;
|
|
11
11
|
return (
|
|
12
12
|
<div className={[
|
|
@@ -31,12 +31,12 @@ const AUNotificationsComponent: FC<AUNotificationsComponentProps> = ({
|
|
|
31
31
|
);
|
|
32
32
|
};
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
AUNotificationComponent.defaultProps = {
|
|
35
35
|
header: null,
|
|
36
36
|
actions: [],
|
|
37
37
|
type: 'default',
|
|
38
38
|
classNames: [],
|
|
39
39
|
};
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
export default
|
|
41
|
+
AUNotificationComponent.displayName = 'AUNotificationComponent';
|
|
42
|
+
export default AUNotificationComponent;
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { ComponentStory, ComponentMeta } from '@storybook/react';
|
|
3
|
+
|
|
4
|
+
import AUErrorComponent from '../src/components/AUErrorComponent';
|
|
5
|
+
|
|
6
|
+
// More on default export: https://storybook.js.org/docs/react/writing-stories/introduction#default-export
|
|
7
|
+
export default {
|
|
8
|
+
title: 'Delphinus/Error',
|
|
9
|
+
component: AUErrorComponent,
|
|
10
|
+
// More on argTypes: https://storybook.js.org/docs/react/api/argtypes
|
|
11
|
+
decorators: [
|
|
12
|
+
(Story) => { // , context) => {
|
|
13
|
+
/*
|
|
14
|
+
const { args: { themeColor, themeMode } } = context;
|
|
15
|
+
const themeColorClass = themeColor === 'none' ? '' : `theme--${themeColor}`;
|
|
16
|
+
const themeModeClass = themeMode === 'light' ? '' : `theme--${themeMode}`;
|
|
17
|
+
*/
|
|
18
|
+
return (
|
|
19
|
+
<div>
|
|
20
|
+
{Story()}
|
|
21
|
+
</div>
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
]
|
|
25
|
+
} as ComponentMeta<typeof AUErrorComponent>;
|
|
26
|
+
|
|
27
|
+
// More on component templates: https://storybook.js.org/docs/react/writing-stories/introduction#using-args
|
|
28
|
+
const Template: ComponentStory<typeof AUErrorComponent> = (args) => <AUErrorComponent {...args} />;
|
|
29
|
+
|
|
30
|
+
export const AU_Error = Template.bind({});
|
|
31
|
+
// More on args: https://storybook.js.org/docs/react/writing-stories/args
|
|
32
|
+
AU_Error.args = {
|
|
33
|
+
error: {
|
|
34
|
+
header: 'Our own custom error',
|
|
35
|
+
message: 'With a nice header and descriptive error message',
|
|
36
|
+
status: 500,
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export const AU_Error_With_Status = Template.bind({});
|
|
41
|
+
// More on args: https://storybook.js.org/docs/react/writing-stories/args
|
|
42
|
+
AU_Error_With_Status.args = {
|
|
43
|
+
error: {
|
|
44
|
+
header: 'Our own custom error',
|
|
45
|
+
message: 'With a nice header and descriptive error message',
|
|
46
|
+
status: 500,
|
|
47
|
+
},
|
|
48
|
+
withStatus: true,
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export const Fetch_Error = Template.bind({});
|
|
52
|
+
// More on args: https://storybook.js.org/docs/react/writing-stories/args
|
|
53
|
+
Fetch_Error.args = {
|
|
54
|
+
fetchError: {
|
|
55
|
+
error: 'Something horrible has happened on the server...',
|
|
56
|
+
status: 'FETCH_ERROR',
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export const Fetch_Error_With_Status = Template.bind({});
|
|
61
|
+
// More on args: https://storybook.js.org/docs/react/writing-stories/args
|
|
62
|
+
Fetch_Error_With_Status.args = {
|
|
63
|
+
fetchError: {
|
|
64
|
+
error: 'Something horrible has happened on the server...',
|
|
65
|
+
status: 'FETCH_ERROR',
|
|
66
|
+
},
|
|
67
|
+
withStatus: true,
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export const Fetch_Error_With_Data = Template.bind({});
|
|
71
|
+
// More on args: https://storybook.js.org/docs/react/writing-stories/args
|
|
72
|
+
Fetch_Error_With_Data.args = {
|
|
73
|
+
fetchError: {
|
|
74
|
+
data: {
|
|
75
|
+
status: 'Error',
|
|
76
|
+
message: 'This could be anything...',
|
|
77
|
+
somethingElse: 'Even this...',
|
|
78
|
+
},
|
|
79
|
+
status: 500,
|
|
80
|
+
},
|
|
81
|
+
withStatus: false,
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
export const Fetch_Error_With_Data_And_Status = Template.bind({});
|
|
85
|
+
// More on args: https://storybook.js.org/docs/react/writing-stories/args
|
|
86
|
+
Fetch_Error_With_Data_And_Status.args = {
|
|
87
|
+
fetchError: {
|
|
88
|
+
data: {
|
|
89
|
+
status: 'Error',
|
|
90
|
+
message: 'This could be anything...',
|
|
91
|
+
somethingElse: 'Even this...',
|
|
92
|
+
},
|
|
93
|
+
status: 500,
|
|
94
|
+
},
|
|
95
|
+
withStatus: true,
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
export const Serialized_Error = Template.bind({});
|
|
99
|
+
// More on args: https://storybook.js.org/docs/react/writing-stories/args
|
|
100
|
+
Serialized_Error.args = {
|
|
101
|
+
serializedError: {
|
|
102
|
+
message: 'Something funky has happened in the code...',
|
|
103
|
+
code: '500',
|
|
104
|
+
},
|
|
105
|
+
withStatus: false,
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
export const Serialized_Error_With_Code = Template.bind({});
|
|
109
|
+
// More on args: https://storybook.js.org/docs/react/writing-stories/args
|
|
110
|
+
Serialized_Error_With_Code.args = {
|
|
111
|
+
serializedError: {
|
|
112
|
+
message: 'Something funky has happened in the code...',
|
|
113
|
+
code: '500',
|
|
114
|
+
},
|
|
115
|
+
withStatus: true,
|
|
116
|
+
};
|
|
117
|
+
|