@aarhus-university/au-lib-react-components 10.21.0 → 10.22.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/.storybook/preview.js
CHANGED
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.22.0",
|
|
5
5
|
"description": "Library for shared React components for various applications on au.dk",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"test": "jest",
|
|
@@ -72,9 +72,9 @@
|
|
|
72
72
|
"webpack-cli": "^4.9.2"
|
|
73
73
|
},
|
|
74
74
|
"dependencies": {
|
|
75
|
-
"@aarhus-university/au-designsystem-delphinus": "0.
|
|
75
|
+
"@aarhus-university/au-designsystem-delphinus": "0.34.2",
|
|
76
76
|
"@aarhus-university/au-designsystem-delphinus-dev": "0.2.0",
|
|
77
|
-
"@aarhus-university/types": "0.
|
|
77
|
+
"@aarhus-university/types": "^0.15.0",
|
|
78
78
|
"@reduxjs/toolkit": "^1.8.3",
|
|
79
79
|
"@types/google.analytics": "^0.0.42",
|
|
80
80
|
"@types/history": "^5.0.0",
|
|
@@ -3,28 +3,33 @@ import React, {
|
|
|
3
3
|
useRef,
|
|
4
4
|
FC,
|
|
5
5
|
} from 'react';
|
|
6
|
+
// import { setContentToggle } from '@aarhus-university/au-desi
|
|
7
|
+
// gnsystem-delphinus/source/js/components/content-toggle';
|
|
6
8
|
import { setContentToggle } from '@aarhus-university/au-designsystem-delphinus/source/js/components/content-toggle';
|
|
7
9
|
|
|
8
10
|
const AUContentToggleComponent: FC<AUContentToggleComponentProps> = ({
|
|
9
11
|
toggled,
|
|
10
12
|
classNames,
|
|
11
13
|
children,
|
|
14
|
+
onClick,
|
|
12
15
|
beforeCallback,
|
|
13
16
|
afterCallback,
|
|
14
17
|
}: AUContentToggleComponentProps) => {
|
|
15
18
|
const toggleContainer = useRef<HTMLDivElement>(null);
|
|
16
19
|
useEffect(() => {
|
|
17
20
|
const toggle = toggleContainer?.current?.querySelector('.content-toggle__content');
|
|
18
|
-
let cleanUp: CleanUpPair
|
|
21
|
+
let cleanUp: CleanUpPair[];
|
|
19
22
|
if (toggle) {
|
|
20
|
-
cleanUp = setContentToggle(toggle, beforeCallback, afterCallback);
|
|
21
|
-
cleanUp
|
|
23
|
+
cleanUp = setContentToggle(toggle, onClick, beforeCallback, afterCallback);
|
|
24
|
+
if (cleanUp.length > 0) { // Den første er altid knappen (og ikke luk-knappen)
|
|
25
|
+
cleanUp[0].element.setAttribute('aria-expanded', toggled ? 'true' : 'false');
|
|
26
|
+
}
|
|
22
27
|
}
|
|
23
28
|
|
|
24
29
|
return () => {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
30
|
+
cleanUp.forEach((c) => {
|
|
31
|
+
c.element.removeEventListener('click', c.clickEvent);
|
|
32
|
+
});
|
|
28
33
|
};
|
|
29
34
|
}, []);
|
|
30
35
|
return (
|
|
@@ -1,52 +1,101 @@
|
|
|
1
1
|
/* eslint-disable react/no-array-index-key */
|
|
2
2
|
/* eslint-env browser */
|
|
3
|
-
import React, { useEffect, FC } from 'react';
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
import React, { useEffect, FC, useRef } from 'react';
|
|
4
|
+
import {
|
|
5
|
+
setToolbar, closeAllContentTogglesInToolbar, ExtendedCleanUpPair, setOverflow,
|
|
6
|
+
} from '@aarhus-university/au-designsystem-delphinus/source/js/components/toolbar';
|
|
7
7
|
|
|
8
8
|
const AUToolbarComponent: FC<AUToolbarComponentProps> = ({
|
|
9
9
|
lang,
|
|
10
10
|
elements,
|
|
11
|
+
noCollapse,
|
|
12
|
+
center,
|
|
13
|
+
title,
|
|
14
|
+
buttonClass,
|
|
15
|
+
buttonElements,
|
|
11
16
|
}: AUToolbarComponentProps) => {
|
|
17
|
+
const toolbarRef = useRef<HTMLDivElement>(null);
|
|
12
18
|
useEffect(() => {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
if (windowInnerWidth > 0 && window.innerWidth !== windowInnerWidth) {
|
|
18
|
-
windowInnerWidth = window.innerWidth;
|
|
19
|
-
setToolbars();
|
|
19
|
+
const onResize = () => {
|
|
20
|
+
if (toolbarRef.current?.querySelector('.toolbar__items')) {
|
|
21
|
+
closeAllContentTogglesInToolbar(toolbarRef.current);
|
|
22
|
+
setOverflow(toolbarRef.current);
|
|
20
23
|
}
|
|
21
|
-
}
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
let toolbarCleanUp: ExtendedCleanUpPair[];
|
|
27
|
+
if (toolbarRef.current) {
|
|
28
|
+
// eslint-disable-next-line max-len
|
|
29
|
+
toolbarCleanUp = setToolbar(toolbarRef.current, (toolbar: HTMLElement) => {
|
|
30
|
+
closeAllContentTogglesInToolbar(toolbar);
|
|
31
|
+
}, false);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
window.addEventListener('resize', onResize);
|
|
22
35
|
|
|
23
36
|
// Runs on unmounting
|
|
24
37
|
return () => {
|
|
25
|
-
|
|
26
|
-
|
|
38
|
+
toolbarCleanUp.forEach((pair: ExtendedCleanUpPair) => {
|
|
39
|
+
if (typeof pair.clickEvent === 'function') {
|
|
40
|
+
pair.element.removeEventListener('click', pair.clickEvent);
|
|
41
|
+
}
|
|
42
|
+
if (typeof pair.keyUpEvent === 'function') {
|
|
43
|
+
pair.element.removeEventListener('keyup', pair.keyUpEvent);
|
|
44
|
+
}
|
|
27
45
|
});
|
|
46
|
+
|
|
47
|
+
window.removeEventListener('resize', onResize);
|
|
28
48
|
};
|
|
29
49
|
}, []);
|
|
30
50
|
|
|
31
51
|
const renderElements = elements.map((e, i) => <React.Fragment key={i}>{e}</React.Fragment>);
|
|
32
52
|
const label = lang === 'da' ? 'Muligheder' : 'Options';
|
|
33
53
|
|
|
54
|
+
let toolbarClass = 'toolbar';
|
|
55
|
+
if (noCollapse) {
|
|
56
|
+
toolbarClass = `${toolbarClass} toolbar--no-collapse`;
|
|
57
|
+
}
|
|
58
|
+
if (center) {
|
|
59
|
+
toolbarClass = `${toolbarClass} toolbar--center-align-items`;
|
|
60
|
+
}
|
|
61
|
+
|
|
34
62
|
return (
|
|
35
|
-
<div
|
|
63
|
+
<div
|
|
64
|
+
ref={toolbarRef}
|
|
65
|
+
className={toolbarClass}
|
|
66
|
+
>
|
|
36
67
|
<div className="toolbar__items">
|
|
37
68
|
{renderElements}
|
|
38
69
|
</div>
|
|
39
70
|
<button
|
|
40
71
|
type="button"
|
|
41
|
-
className=
|
|
42
|
-
title={label}
|
|
72
|
+
className={buttonClass}
|
|
73
|
+
title={title || label}
|
|
74
|
+
aria-label={title}
|
|
43
75
|
aria-haspopup="true"
|
|
44
76
|
>
|
|
45
|
-
{
|
|
77
|
+
{
|
|
78
|
+
buttonElements?.length === 0 && (title || label)
|
|
79
|
+
}
|
|
80
|
+
{
|
|
81
|
+
(buttonElements || []).map((element, index) => (
|
|
82
|
+
<React.Fragment key={index}>
|
|
83
|
+
{element}
|
|
84
|
+
</React.Fragment>
|
|
85
|
+
))
|
|
86
|
+
}
|
|
46
87
|
</button>
|
|
47
88
|
</div>
|
|
48
89
|
);
|
|
49
90
|
};
|
|
50
91
|
|
|
92
|
+
AUToolbarComponent.defaultProps = {
|
|
93
|
+
noCollapse: false,
|
|
94
|
+
center: false,
|
|
95
|
+
title: undefined,
|
|
96
|
+
buttonClass: 'toolbar__toggle',
|
|
97
|
+
buttonElements: [],
|
|
98
|
+
};
|
|
99
|
+
|
|
51
100
|
AUToolbarComponent.displayName = 'AUToolbarComponent';
|
|
52
101
|
export default AUToolbarComponent;
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import React, {
|
|
2
|
+
FC, useEffect, useRef, useState,
|
|
3
|
+
} from 'react';
|
|
4
|
+
import { overflowsY } from '@aarhus-university/au-designsystem-delphinus/source/js/components/helpers';
|
|
5
|
+
|
|
6
|
+
const focusableSelector = 'a[href]:not([tabindex="-1"]), area[href]:not([tabindex="-1"]), input:not([disabled]):not([tabindex="-1"]), select:not([disabled]):not([tabindex="-1"]), textarea:not([disabled]):not([tabindex="-1"]), button:not([disabled]):not([tabindex="-1"]), iframe:not([tabindex="-1"]), [tabindex]:not([tabindex="-1"]), [contentEditable=true]:not([tabindex="-1"])';
|
|
7
|
+
const getMaxLinesStyle = (maxLines: number): React.CSSProperties => ({ '--max-lines': maxLines } as React.CSSProperties);
|
|
8
|
+
|
|
9
|
+
const setTruncate = (
|
|
10
|
+
expandElement: HTMLElement,
|
|
11
|
+
contentElement: HTMLElement,
|
|
12
|
+
expanded: boolean,
|
|
13
|
+
): void => {
|
|
14
|
+
if (!expanded && overflowsY(contentElement)) {
|
|
15
|
+
expandElement.removeAttribute('hidden');
|
|
16
|
+
} else {
|
|
17
|
+
expandElement.parentElement?.classList.add('truncator--no-truncation');
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const AUTruncatorComponent: FC<AUTruncatorComponentProps> = ({
|
|
22
|
+
maxLines,
|
|
23
|
+
header,
|
|
24
|
+
headerLevel,
|
|
25
|
+
children,
|
|
26
|
+
classNames,
|
|
27
|
+
buttonChild,
|
|
28
|
+
}: AUTruncatorComponentProps) => {
|
|
29
|
+
const expandRef = useRef<HTMLButtonElement>(null);
|
|
30
|
+
const contentRef = useRef<HTMLDivElement>(null);
|
|
31
|
+
const [expanded, setExpanded] = useState<boolean>(false);
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
setTruncate(expandRef.current as HTMLElement, contentRef.current as HTMLElement, expanded);
|
|
34
|
+
const focusElements = contentRef.current?.querySelectorAll(focusableSelector);
|
|
35
|
+
|
|
36
|
+
const onFocus = (event: Event) => {
|
|
37
|
+
if (overflowsY(event.target as HTMLElement)) {
|
|
38
|
+
setExpanded(true);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
if (focusElements) {
|
|
43
|
+
focusElements.forEach((f) => {
|
|
44
|
+
f.addEventListener('focus', onFocus);
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const onResize = () => {
|
|
49
|
+
setTruncate(expandRef.current as HTMLElement, contentRef.current as HTMLElement, expanded);
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
window.addEventListener('resize', onResize);
|
|
53
|
+
|
|
54
|
+
return () => {
|
|
55
|
+
if (focusElements) {
|
|
56
|
+
focusElements.forEach((f) => {
|
|
57
|
+
f.removeEventListener('focus', onFocus);
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
window.removeEventListener('resize', onResize);
|
|
61
|
+
};
|
|
62
|
+
}, [expanded]);
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<div className={`${classNames}${expanded ? ' truncator--no-truncation' : ''}`} style={getMaxLinesStyle(maxLines)}>
|
|
66
|
+
{
|
|
67
|
+
header && (
|
|
68
|
+
<>
|
|
69
|
+
{
|
|
70
|
+
headerLevel === 1 && (
|
|
71
|
+
<h1 className="truncator__header">{header}</h1>
|
|
72
|
+
)
|
|
73
|
+
}
|
|
74
|
+
{
|
|
75
|
+
headerLevel === 2 && (
|
|
76
|
+
<h2 className="truncator__header">{header}</h2>
|
|
77
|
+
)
|
|
78
|
+
}
|
|
79
|
+
{
|
|
80
|
+
headerLevel === 3 && (
|
|
81
|
+
<h3 className="truncator__header">{header}</h3>
|
|
82
|
+
)
|
|
83
|
+
}
|
|
84
|
+
{
|
|
85
|
+
headerLevel === 4 && (
|
|
86
|
+
<h4 className="truncator__header">{header}</h4>
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
{
|
|
90
|
+
headerLevel === 5 && (
|
|
91
|
+
<h5 className="truncator__header">{header}</h5>
|
|
92
|
+
)
|
|
93
|
+
}
|
|
94
|
+
{
|
|
95
|
+
headerLevel === 6 && (
|
|
96
|
+
<h6 className="truncator__header">{header}</h6>
|
|
97
|
+
)
|
|
98
|
+
}
|
|
99
|
+
</>
|
|
100
|
+
)
|
|
101
|
+
}
|
|
102
|
+
<button
|
|
103
|
+
ref={expandRef}
|
|
104
|
+
type="button"
|
|
105
|
+
className="truncator__expand"
|
|
106
|
+
hidden
|
|
107
|
+
aria-expanded={expanded}
|
|
108
|
+
onClick={() => setExpanded(true)}
|
|
109
|
+
>
|
|
110
|
+
{buttonChild}
|
|
111
|
+
</button>
|
|
112
|
+
<div
|
|
113
|
+
ref={contentRef}
|
|
114
|
+
className="truncator__content"
|
|
115
|
+
>
|
|
116
|
+
{children}
|
|
117
|
+
</div>
|
|
118
|
+
</div>
|
|
119
|
+
);
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
AUTruncatorComponent.defaultProps = {
|
|
123
|
+
header: undefined,
|
|
124
|
+
headerLevel: 2,
|
|
125
|
+
classNames: 'truncator',
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
AUTruncatorComponent.displayName = 'AUTruncatorComponent';
|
|
129
|
+
export default AUTruncatorComponent;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { ComponentStory, ComponentMeta } from '@storybook/react';
|
|
3
|
-
import { closeAllContentTogglesInToolbar } from '@aarhus-university/au-designsystem-delphinus/source/js/components/toolbar';
|
|
3
|
+
import { closeAllContentTogglesInToolbar, findToolbar } from '@aarhus-university/au-designsystem-delphinus/source/js/components/toolbar';
|
|
4
4
|
import AUToolbarComponent from '../src/components/AUToolbarComponent';
|
|
5
5
|
import AUButtonComponent from '../src/components/AUButtonComponent';
|
|
6
6
|
import AUContentToggleComponent from '../src/components/AUContentToggleComponent';
|
|
@@ -102,9 +102,9 @@ Filter.args = {
|
|
|
102
102
|
elements: [
|
|
103
103
|
<AUContentToggleComponent
|
|
104
104
|
toggled={false}
|
|
105
|
-
beforeCallback={() => closeAllContentTogglesInToolbar(
|
|
106
|
-
afterCallback={(button: HTMLButtonElement) => {
|
|
107
|
-
const toolbar =
|
|
105
|
+
beforeCallback={(content: HTMLElement) => closeAllContentTogglesInToolbar(findToolbar(content))}
|
|
106
|
+
afterCallback={(content: HTMLElement, button: HTMLButtonElement) => {
|
|
107
|
+
const toolbar = findToolbar(content);
|
|
108
108
|
const ariaExpanded = button.getAttribute('aria-expanded');
|
|
109
109
|
if (ariaExpanded === 'true') {
|
|
110
110
|
toolbar?.classList.add('toolbar--has-popout');
|
|
@@ -157,9 +157,9 @@ Filter.args = {
|
|
|
157
157
|
</AUContentToggleComponent>,
|
|
158
158
|
<AUContentToggleComponent
|
|
159
159
|
toggled={false}
|
|
160
|
-
beforeCallback={() => closeAllContentTogglesInToolbar(
|
|
161
|
-
afterCallback={(button: HTMLButtonElement) => {
|
|
162
|
-
const toolbar =
|
|
160
|
+
beforeCallback={(content: HTMLElement) => closeAllContentTogglesInToolbar(findToolbar(content))}
|
|
161
|
+
afterCallback={(content: HTMLElement, button: HTMLButtonElement) => {
|
|
162
|
+
const toolbar = findToolbar(content);
|
|
163
163
|
const ariaExpanded = button.getAttribute('aria-expanded');
|
|
164
164
|
if (ariaExpanded === 'true') {
|
|
165
165
|
toolbar?.classList.add('toolbar--has-popout');
|