@aarhus-university/au-lib-react-components 12.0.2 → 12.1.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 +1 -1
- package/package.json +15 -14
- package/src/components/AUCharacterCountComponent.tsx +56 -0
- package/src/components/AUEditorComponent.tsx +123 -0
- package/src/lib/hooks.ts +83 -1
- package/src/lib/tinymce.ts +84 -0
- package/stories/AUAlertComponent.stories.tsx +37 -25
- package/stories/AUAutoSuggestComponent.stories.tsx +69 -70
- package/stories/AUButtonComponent.stories.tsx +62 -51
- package/stories/AUCharacterCountComponent.stories.tsx +146 -0
- package/stories/AUComboBoxComponent.stories.tsx +13 -12
- package/stories/AUContentToggleComponent.stories.tsx +54 -30
- package/stories/AUEditorComponent.stories.tsx +68 -0
- package/stories/AUErrorComponent.stories.tsx +93 -58
- package/stories/AUModalComponent.stories.tsx +124 -48
- package/stories/AUNotificationComponent.stories.tsx +107 -71
- package/stories/AUSpinnerComponent.stories.tsx +15 -12
- package/stories/AUStepComponent.stories.tsx +70 -41
- package/stories/AUToolbarComponent.stories.tsx +338 -276
- package/stories/AUTruncatorComponent.stories.tsx +87 -66
package/.storybook/preview.js
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"sideEffects": true,
|
|
3
3
|
"name": "@aarhus-university/au-lib-react-components",
|
|
4
|
-
"version": "12.0
|
|
4
|
+
"version": "12.1.0",
|
|
5
5
|
"description": "Library for shared React components for various applications on au.dk",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"test": "jest",
|
|
@@ -29,15 +29,15 @@
|
|
|
29
29
|
"@babel/preset-react": "^7.22.5",
|
|
30
30
|
"@babel/preset-typescript": "^7.22.5",
|
|
31
31
|
"@mdx-js/react": "^2.3.0",
|
|
32
|
-
"@storybook/addon-a11y": "^7.
|
|
33
|
-
"@storybook/addon-actions": "^7.
|
|
34
|
-
"@storybook/addon-essentials": "^7.
|
|
35
|
-
"@storybook/addon-interactions": "^7.
|
|
36
|
-
"@storybook/addon-links": "^7.
|
|
37
|
-
"@storybook/jest": "^0.
|
|
38
|
-
"@storybook/react": "^7.
|
|
39
|
-
"@storybook/react-webpack5": "^7.
|
|
40
|
-
"@storybook/testing-library": "^0.2.
|
|
32
|
+
"@storybook/addon-a11y": "^7.6.7",
|
|
33
|
+
"@storybook/addon-actions": "^7.6.7",
|
|
34
|
+
"@storybook/addon-essentials": "^7.6.7",
|
|
35
|
+
"@storybook/addon-interactions": "^7.6.7",
|
|
36
|
+
"@storybook/addon-links": "^7.6.7",
|
|
37
|
+
"@storybook/jest": "^0.2.3",
|
|
38
|
+
"@storybook/react": "^7.6.7",
|
|
39
|
+
"@storybook/react-webpack5": "^7.6.7",
|
|
40
|
+
"@storybook/testing-library": "^0.2.2",
|
|
41
41
|
"@testing-library/jest-dom": "^5.16.5",
|
|
42
42
|
"@testing-library/react": "^14.0.0",
|
|
43
43
|
"@testing-library/user-event": "^14.4.3",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"eslint-plugin-jsx-a11y": "^6.7.1",
|
|
57
57
|
"eslint-plugin-react": "^7.32.2",
|
|
58
58
|
"eslint-plugin-react-hooks": "^4.6.0",
|
|
59
|
-
"eslint-plugin-storybook": "^0.6.
|
|
59
|
+
"eslint-plugin-storybook": "^0.6.15",
|
|
60
60
|
"eslint-webpack-plugin": "^4.0.1",
|
|
61
61
|
"fork-ts-checker-webpack-plugin": "^8.0.0",
|
|
62
62
|
"jest": "^29.5.0",
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
"react-test-renderer": "^18.2.0",
|
|
67
67
|
"sass": "^1.63.6",
|
|
68
68
|
"sass-loader": "^13.3.2",
|
|
69
|
-
"storybook": "^7.
|
|
69
|
+
"storybook": "^7.6.7",
|
|
70
70
|
"style-loader": "^3.3.3",
|
|
71
71
|
"ts-jest": "^29.1.1",
|
|
72
72
|
"ts-loader": "^9.4.4",
|
|
@@ -74,10 +74,11 @@
|
|
|
74
74
|
"webpack-cli": "^5.1.4"
|
|
75
75
|
},
|
|
76
76
|
"dependencies": {
|
|
77
|
-
"@aarhus-university/au-designsystem-delphinus": "
|
|
77
|
+
"@aarhus-university/au-designsystem-delphinus": "1.3.3",
|
|
78
78
|
"@aarhus-university/au-designsystem-delphinus-dev": "0.7.0",
|
|
79
|
-
"@aarhus-university/types": "1.0.
|
|
79
|
+
"@aarhus-university/types": "1.0.1",
|
|
80
80
|
"@reduxjs/toolkit": "^1.9.5",
|
|
81
|
+
"@tinymce/tinymce-react": "^4.3.2",
|
|
81
82
|
"@types/google.analytics": "^0.0.42",
|
|
82
83
|
"@types/react": "^18.2.14",
|
|
83
84
|
"@types/react-dom": "^18.2.6",
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/* eslint-disable camelcase */
|
|
2
|
+
import React, {
|
|
3
|
+
FC,
|
|
4
|
+
} from 'react';
|
|
5
|
+
import { replace } from '../lib/helpers';
|
|
6
|
+
import { useCharacterCount } from '../lib/hooks';
|
|
7
|
+
|
|
8
|
+
type Props = {
|
|
9
|
+
children: React.ReactNode;
|
|
10
|
+
type: 'recommended' | 'allowed';
|
|
11
|
+
initialChars: number;
|
|
12
|
+
maxChars: number;
|
|
13
|
+
translations: IGenericTranslation['generics']['forms'];
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const AUCharacterCountComponent: FC<Props> = ({
|
|
17
|
+
children,
|
|
18
|
+
type,
|
|
19
|
+
initialChars,
|
|
20
|
+
maxChars,
|
|
21
|
+
translations,
|
|
22
|
+
}) => {
|
|
23
|
+
const {
|
|
24
|
+
ref, remaining, overflow,
|
|
25
|
+
} = useCharacterCount(type, initialChars, maxChars);
|
|
26
|
+
|
|
27
|
+
const { character_count } = translations;
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<div
|
|
31
|
+
ref={ref}
|
|
32
|
+
className="form__field__character-count"
|
|
33
|
+
>
|
|
34
|
+
{children}
|
|
35
|
+
<div
|
|
36
|
+
className="form__field__character-count__info"
|
|
37
|
+
aria-live="polite"
|
|
38
|
+
>
|
|
39
|
+
<span className="form__field__character-count__info__overflow-message">
|
|
40
|
+
{`${replace(character_count.overflow.message[type], ['maxchars'], [maxChars.toString()])}`}
|
|
41
|
+
</span>
|
|
42
|
+
{' '}
|
|
43
|
+
<span className="form__field__character-count__info__overflow-count">
|
|
44
|
+
{`${replace(character_count.overflow.count[type][overflow === 1 ? 'one' : 'other'], ['overflow'], [overflow.toString()])}`}
|
|
45
|
+
</span>
|
|
46
|
+
{' '}
|
|
47
|
+
<span className="form__field__character-count__info__count-down">
|
|
48
|
+
{`${replace(character_count.count_down[remaining === 1 ? 'one' : 'other'], ['remaining'], [remaining.toString()])}`}
|
|
49
|
+
</span>
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
AUCharacterCountComponent.displayName = 'AUCharacterCountComponent';
|
|
56
|
+
export default AUCharacterCountComponent;
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/* eslint-disable camelcase */
|
|
2
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
3
|
+
import React, { FC, useState, useRef } from 'react';
|
|
4
|
+
import { Editor } from '@tinymce/tinymce-react';
|
|
5
|
+
import AUTinyMCEBuilder from '../lib/tinymce';
|
|
6
|
+
import { replace } from '../lib/helpers';
|
|
7
|
+
|
|
8
|
+
type Translation = IGenericTranslation['generics']['forms'];
|
|
9
|
+
type CounterType = 'recommended' | 'allowed';
|
|
10
|
+
type Props = {
|
|
11
|
+
lang: string;
|
|
12
|
+
id: string;
|
|
13
|
+
initialValue: string;
|
|
14
|
+
textareaName: string;
|
|
15
|
+
showCounter?: boolean;
|
|
16
|
+
counterType?: CounterType;
|
|
17
|
+
maxChars?: number;
|
|
18
|
+
translations: Translation;
|
|
19
|
+
onEditorChange: (value: string) => void;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const onKeyUp = (
|
|
23
|
+
editor: any,
|
|
24
|
+
parent: HTMLDivElement,
|
|
25
|
+
maxChars: number,
|
|
26
|
+
counterType: string,
|
|
27
|
+
setOverflow: React.Dispatch<React.SetStateAction<number>>,
|
|
28
|
+
): void => {
|
|
29
|
+
const container = editor.getContainer();
|
|
30
|
+
const numChars = editor.plugins.wordcount.body.getCharacterCount();
|
|
31
|
+
let isActive = false;
|
|
32
|
+
if (numChars >= maxChars) {
|
|
33
|
+
isActive = true;
|
|
34
|
+
parent.setAttribute(`data-${counterType}-length`, maxChars.toString());
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (isActive) {
|
|
38
|
+
container.classList.add('info-active');
|
|
39
|
+
} else {
|
|
40
|
+
container.classList.remove('info-active');
|
|
41
|
+
parent.removeAttribute(`data-${counterType}-length`);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const overflow = numChars - maxChars;
|
|
45
|
+
if (overflow > 0) {
|
|
46
|
+
parent.classList.add('form__field__character-count--overflow');
|
|
47
|
+
} else {
|
|
48
|
+
parent.classList.remove('form__field__character-count--overflow');
|
|
49
|
+
}
|
|
50
|
+
setOverflow(overflow);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const AUEditorComponent: FC<Props> = ({
|
|
54
|
+
lang,
|
|
55
|
+
id,
|
|
56
|
+
initialValue,
|
|
57
|
+
textareaName,
|
|
58
|
+
showCounter,
|
|
59
|
+
counterType,
|
|
60
|
+
maxChars,
|
|
61
|
+
translations,
|
|
62
|
+
onEditorChange,
|
|
63
|
+
}) => {
|
|
64
|
+
const [overflow, setOverflow] = useState(0);
|
|
65
|
+
const parentRef = useRef<HTMLDivElement>(null);
|
|
66
|
+
const tiny = new AUTinyMCEBuilder(
|
|
67
|
+
lang,
|
|
68
|
+
showCounter as boolean,
|
|
69
|
+
(editor: any) => onKeyUp(
|
|
70
|
+
editor,
|
|
71
|
+
parentRef.current as HTMLDivElement,
|
|
72
|
+
maxChars as number,
|
|
73
|
+
counterType as string,
|
|
74
|
+
setOverflow,
|
|
75
|
+
),
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
const { character_count } = translations;
|
|
79
|
+
|
|
80
|
+
return (
|
|
81
|
+
<div
|
|
82
|
+
ref={parentRef}
|
|
83
|
+
className="form__field__character-count"
|
|
84
|
+
>
|
|
85
|
+
<Editor
|
|
86
|
+
tinymceScriptSrc={tiny.tinyScriptSrc}
|
|
87
|
+
initialValue={initialValue}
|
|
88
|
+
id={id}
|
|
89
|
+
// value={tinyDanish}
|
|
90
|
+
textareaName={textareaName}
|
|
91
|
+
init={tiny.tinyGeneralConfig()}
|
|
92
|
+
onEditorChange={onEditorChange}
|
|
93
|
+
/>
|
|
94
|
+
{
|
|
95
|
+
(typeof showCounter !== 'undefined'
|
|
96
|
+
&& typeof counterType !== 'undefined'
|
|
97
|
+
&& typeof maxChars !== 'undefined') && (
|
|
98
|
+
<div
|
|
99
|
+
className="form__field__character-count__info"
|
|
100
|
+
aria-live="polite"
|
|
101
|
+
>
|
|
102
|
+
<span className="form__field__character-count__info__overflow-message">
|
|
103
|
+
{`${replace(character_count.overflow.message[counterType], ['maxchars'], [maxChars.toString()])}`}
|
|
104
|
+
</span>
|
|
105
|
+
{' '}
|
|
106
|
+
<span className="form__field__character-count__info__overflow-count">
|
|
107
|
+
{`${replace(character_count.overflow.count[counterType][overflow === 1 ? 'one' : 'other'], ['overflow'], [overflow.toString()])}`}
|
|
108
|
+
</span>
|
|
109
|
+
</div>
|
|
110
|
+
)
|
|
111
|
+
}
|
|
112
|
+
</div>
|
|
113
|
+
);
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
AUEditorComponent.defaultProps = {
|
|
117
|
+
showCounter: false,
|
|
118
|
+
counterType: 'recommended',
|
|
119
|
+
maxChars: 0,
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
AUEditorComponent.displayName = 'AUEditorComponent';
|
|
123
|
+
export default AUEditorComponent;
|
package/src/lib/hooks.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
2
|
/* eslint-disable @typescript-eslint/no-empty-function */
|
|
3
3
|
/* eslint-disable import/prefer-default-export */
|
|
4
|
-
import { useEffect, useState } from 'react';
|
|
4
|
+
import { useEffect, useState, useRef } from 'react';
|
|
5
5
|
import { showModal, hideModal } from '@aarhus-university/au-designsystem-delphinus/source/js/components/modal-view';
|
|
6
6
|
|
|
7
7
|
const modalIsVisible = (modalElement: Element | null) => (!modalElement ? false : getComputedStyle(modalElement).display !== 'none');
|
|
@@ -69,7 +69,89 @@ const useTranslation = <T>(
|
|
|
69
69
|
return translations;
|
|
70
70
|
};
|
|
71
71
|
|
|
72
|
+
const isTextArea = (element: HTMLInputElement): boolean => element.tagName === 'TEXTAREA';
|
|
73
|
+
|
|
74
|
+
const setAutoExpand = (input: HTMLInputElement, parent: HTMLElement): void => {
|
|
75
|
+
if (isTextArea(input)) {
|
|
76
|
+
parent.classList.add('form__field__textarea-autoexpand');
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const setDataValue = (formFieldElement: HTMLElement, inputElement: HTMLInputElement): void => {
|
|
81
|
+
if (isTextArea(inputElement)) {
|
|
82
|
+
formFieldElement.setAttribute('data-value', inputElement.value);
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const setText = (
|
|
87
|
+
formField: HTMLElement,
|
|
88
|
+
overflow: number,
|
|
89
|
+
): void => {
|
|
90
|
+
if (formField) {
|
|
91
|
+
const inputField = formField.querySelector('input[type="text"], textarea');
|
|
92
|
+
if (inputField) {
|
|
93
|
+
setDataValue(formField, inputField as HTMLInputElement);
|
|
94
|
+
}
|
|
95
|
+
if (overflow > 0) {
|
|
96
|
+
formField.classList.add('form__field__character-count--overflow');
|
|
97
|
+
} else {
|
|
98
|
+
formField.classList.remove('form__field__character-count--overflow');
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const useCharacterCount = (
|
|
104
|
+
type: string,
|
|
105
|
+
initialChars: number,
|
|
106
|
+
maxChars: number,
|
|
107
|
+
): {
|
|
108
|
+
ref: React.RefObject<HTMLDivElement>,
|
|
109
|
+
remaining: number;
|
|
110
|
+
overflow: number;
|
|
111
|
+
} => {
|
|
112
|
+
const ref = useRef<HTMLDivElement>(null);
|
|
113
|
+
const [count, setCount] = useState<number>(0);
|
|
114
|
+
const remaining = maxChars - count;
|
|
115
|
+
const overflow = count - maxChars;
|
|
116
|
+
|
|
117
|
+
useEffect(() => {
|
|
118
|
+
const inputEvent = (event: Event) => {
|
|
119
|
+
if (event.target) {
|
|
120
|
+
setDataValue(ref.current as HTMLElement, event.target as HTMLInputElement);
|
|
121
|
+
}
|
|
122
|
+
setCount((event.target as HTMLInputElement).value.length);
|
|
123
|
+
};
|
|
124
|
+
const inputField = ref.current?.querySelector('input[type="text"], textarea');
|
|
125
|
+
if (inputField) {
|
|
126
|
+
ref.current?.setAttribute(`data-${type}-length`, maxChars.toString());
|
|
127
|
+
setAutoExpand(inputField as HTMLInputElement, ref.current as HTMLElement);
|
|
128
|
+
inputField.addEventListener('input', inputEvent);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return () => {
|
|
132
|
+
if (inputField) {
|
|
133
|
+
inputField.removeEventListener('input', inputEvent);
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
}, []);
|
|
137
|
+
|
|
138
|
+
useEffect(() => {
|
|
139
|
+
setCount(initialChars);
|
|
140
|
+
}, [initialChars]);
|
|
141
|
+
|
|
142
|
+
useEffect(() => {
|
|
143
|
+
setText(ref.current as HTMLElement, overflow);
|
|
144
|
+
}, [count]);
|
|
145
|
+
|
|
146
|
+
return {
|
|
147
|
+
ref,
|
|
148
|
+
remaining,
|
|
149
|
+
overflow,
|
|
150
|
+
};
|
|
151
|
+
};
|
|
152
|
+
|
|
72
153
|
export {
|
|
73
154
|
useModal,
|
|
74
155
|
useTranslation,
|
|
156
|
+
useCharacterCount,
|
|
75
157
|
};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
const blockNames = {
|
|
3
|
+
p: {
|
|
4
|
+
da: 'Afsnit',
|
|
5
|
+
en: 'Paragraph',
|
|
6
|
+
},
|
|
7
|
+
h1: {
|
|
8
|
+
da: 'Overskrift 1',
|
|
9
|
+
en: 'Header 1',
|
|
10
|
+
},
|
|
11
|
+
h2: {
|
|
12
|
+
da: 'Overskrift 2',
|
|
13
|
+
en: 'Header 2',
|
|
14
|
+
},
|
|
15
|
+
h3: {
|
|
16
|
+
da: 'Overskrift 3',
|
|
17
|
+
en: 'Header 3',
|
|
18
|
+
},
|
|
19
|
+
h4: {
|
|
20
|
+
da: 'Overskrift 4',
|
|
21
|
+
en: 'Header 4',
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
class AUTinyMCEBuilder {
|
|
26
|
+
#tinyBase = 'https://cdn.au.dk/vendor/tinymce/6.7.0/tinymce/js/tinymce/';
|
|
27
|
+
|
|
28
|
+
#tinySkin = 'au_delphinus';
|
|
29
|
+
|
|
30
|
+
tinyScriptSrc = `${this.#tinyBase}tinymce.min.js`;
|
|
31
|
+
|
|
32
|
+
#tinyContentCssSrc = `${this.#tinyBase}skins/content/${this.#tinySkin}/content.css`;
|
|
33
|
+
|
|
34
|
+
#lang = 'da';
|
|
35
|
+
|
|
36
|
+
#showCounter = false;
|
|
37
|
+
|
|
38
|
+
#onKeyUp: (editor: any) => void;
|
|
39
|
+
|
|
40
|
+
constructor(
|
|
41
|
+
lang: string,
|
|
42
|
+
showCounter: boolean,
|
|
43
|
+
onKeyUp: (editor: any) => void,
|
|
44
|
+
) {
|
|
45
|
+
this.#lang = lang;
|
|
46
|
+
this.#showCounter = showCounter;
|
|
47
|
+
this.#onKeyUp = onKeyUp;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
tinyGeneralConfig() {
|
|
51
|
+
return {
|
|
52
|
+
skin: this.#tinySkin,
|
|
53
|
+
content_css: this.#tinyContentCssSrc,
|
|
54
|
+
language: this.#lang,
|
|
55
|
+
menubar: false,
|
|
56
|
+
branding: false,
|
|
57
|
+
height: '280',
|
|
58
|
+
plugins: ['lists', 'link', 'visualblocks', 'visualchars', 'wordcount'],
|
|
59
|
+
toolbar: 'blocks | bold italic underline | bullist numlist | link | removeformat visualblocks visualchars',
|
|
60
|
+
// eslint-disable-next-line max-len
|
|
61
|
+
// block_formats: 'Afsnit=p;Overskrift 1=h1;Overskrift 2=h2;Overskrift 3=h3;Overskrift 4=h4;Overskrift 5=h5;Overskrift 6=h6',
|
|
62
|
+
block_formats: Object.keys(blockNames).map((key) => `${blockNames[key][this.#lang]}=${key}`).join(';'),
|
|
63
|
+
setup: (editor: any) => {
|
|
64
|
+
if (this.#showCounter) {
|
|
65
|
+
editor.on('keyup', () => {
|
|
66
|
+
this.#onKeyUp(editor);
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
init_instance_callback: (editor: any) => {
|
|
71
|
+
if (this.#showCounter) {
|
|
72
|
+
const container = editor.getContainer();
|
|
73
|
+
|
|
74
|
+
const button = container.querySelector('button.tox-statusbar__wordcount');
|
|
75
|
+
if (button) {
|
|
76
|
+
button.click();
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export default AUTinyMCEBuilder;
|
|
@@ -1,9 +1,19 @@
|
|
|
1
1
|
import React, { FC, useState } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { StoryObj, Meta } from '@storybook/react';
|
|
3
3
|
import AUAlertComponent, { Props as AlertProps } from '../src/components/AUAlertComponent';
|
|
4
4
|
import { ThemeWrapper } from './lib/helpers';
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
type Props = {
|
|
7
|
+
confirm: boolean;
|
|
8
|
+
label: string;
|
|
9
|
+
mode: AlertProps['mode'];
|
|
10
|
+
header: string;
|
|
11
|
+
message: string;
|
|
12
|
+
okButtonText: string;
|
|
13
|
+
cancelButtonText: string;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const AlertWrapper: FC<Props> = ({
|
|
7
17
|
confirm,
|
|
8
18
|
label,
|
|
9
19
|
mode,
|
|
@@ -42,7 +52,7 @@ const AlertWrapper: FC<AlertProps> = ({
|
|
|
42
52
|
|
|
43
53
|
export default {
|
|
44
54
|
title: 'Delphinus/Alert and Confirm',
|
|
45
|
-
component:
|
|
55
|
+
component: AlertWrapper,
|
|
46
56
|
argTypes: {
|
|
47
57
|
label: {
|
|
48
58
|
table: {
|
|
@@ -92,30 +102,32 @@ export default {
|
|
|
92
102
|
</ThemeWrapper>
|
|
93
103
|
)
|
|
94
104
|
],
|
|
95
|
-
} as
|
|
96
|
-
|
|
97
|
-
const Template: ComponentStory<typeof AUAlertComponent> = (args) => <AlertWrapper {...args} />;
|
|
105
|
+
} as Meta<typeof AlertWrapper>;
|
|
98
106
|
|
|
99
|
-
|
|
107
|
+
type Story = StoryObj<typeof AlertWrapper>;
|
|
100
108
|
|
|
101
|
-
Alert
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
109
|
+
export const Alert: Story = {
|
|
110
|
+
args: {
|
|
111
|
+
confirm: false,
|
|
112
|
+
label: 'Slet',
|
|
113
|
+
mode: 'ireversable-action',
|
|
114
|
+
header: 'Advarsel',
|
|
115
|
+
message: '<p>Den valgte fil vil blive slettet.</p>',
|
|
116
|
+
okButtonText: 'Ok, forstået',
|
|
117
|
+
cancelButtonText: 'Annuller',
|
|
118
|
+
},
|
|
119
|
+
render: (args) => <AlertWrapper {...args} />,
|
|
108
120
|
};
|
|
109
121
|
|
|
110
|
-
export const Confirm =
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
122
|
+
export const Confirm: Story = {
|
|
123
|
+
args: {
|
|
124
|
+
confirm: true,
|
|
125
|
+
label: 'Slet',
|
|
126
|
+
mode: 'ireversable-action',
|
|
127
|
+
header: 'Bekræft',
|
|
128
|
+
message: '<p>Er du sikker på, at du vil slette den valgte fil?</p>',
|
|
129
|
+
okButtonText: 'Ja',
|
|
130
|
+
cancelButtonText: 'Annuller',
|
|
131
|
+
},
|
|
132
|
+
render: (args) => <AlertWrapper {...args} />,
|
|
121
133
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { StoryObj, Meta } from '@storybook/react';
|
|
3
3
|
import AUAutoSuggestComponent from '../src/components/AUAutoSuggestComponent';
|
|
4
4
|
import { ThemeWrapper } from './lib/helpers';
|
|
5
5
|
|
|
@@ -20,78 +20,77 @@ export default {
|
|
|
20
20
|
</ThemeWrapper>
|
|
21
21
|
)
|
|
22
22
|
],
|
|
23
|
-
} as
|
|
23
|
+
} as Meta<typeof AUAutoSuggestComponent>;
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
type Story = StoryObj<typeof AUAutoSuggestComponent>;
|
|
26
26
|
|
|
27
|
-
export const AutoComplete =
|
|
27
|
+
export const AutoComplete: Story = {
|
|
28
|
+
args: {
|
|
29
|
+
collection: [],
|
|
30
|
+
getSuggestions: (value, _, callback) => {
|
|
31
|
+
const inputValue = value.trim().toLowerCase();
|
|
32
|
+
const inputLength = inputValue.length;
|
|
28
33
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
+
const results = inputLength === 0
|
|
35
|
+
? []
|
|
36
|
+
: [
|
|
37
|
+
{ id: 1, name: 'Copenhagen' },
|
|
38
|
+
{ id: 2, name: 'Aarhus' },
|
|
39
|
+
{ id: 3, name: 'Odense' },
|
|
40
|
+
{ id: 4, name: 'Aalborg' },
|
|
41
|
+
{ id: 5, name: 'Esbjerg' },
|
|
42
|
+
{ id: 6, name: 'Randers' },
|
|
43
|
+
{ id: 7, name: 'Kolding' },
|
|
44
|
+
{ id: 8, name: 'Horsens' },
|
|
45
|
+
{ id: 9, name: 'Vejle' },
|
|
46
|
+
{ id: 10, name: 'Herning' },
|
|
47
|
+
{ id: 11, name: 'Frederiksberg' },
|
|
48
|
+
{ id: 12, name: 'Fredericia' },
|
|
49
|
+
{ id: 13, name: 'Roskilde' },
|
|
50
|
+
{ id: 14, name: 'Silkeborg' },
|
|
51
|
+
{ id: 15, name: 'Næstved' },
|
|
52
|
+
{ id: 16, name: 'Helsingør' },
|
|
53
|
+
{ id: 17, name: 'Hvidovre' },
|
|
54
|
+
{ id: 18, name: 'Holstebro' },
|
|
55
|
+
{ id: 19, name: 'Hjørring' },
|
|
56
|
+
{ id: 20, name: 'Glostrup' },
|
|
57
|
+
{ id: 21, name: 'Hillerød' },
|
|
58
|
+
{ id: 22, name: 'Slagelse' },
|
|
59
|
+
{ id: 23, name: 'Viborg' },
|
|
60
|
+
{ id: 24, name: 'Ikast-Brande' },
|
|
61
|
+
{ id: 25, name: 'Frederikshavn' },
|
|
62
|
+
{ id: 26, name: 'Ringsted' },
|
|
63
|
+
{ id: 27, name: 'Taastrup' },
|
|
64
|
+
{ id: 28, name: 'Køge' },
|
|
65
|
+
{ id: 29, name: 'Nørresundby' },
|
|
66
|
+
{ id: 30, name: 'Haderslev' },
|
|
67
|
+
{ id: 31, name: 'Varde' },
|
|
68
|
+
{ id: 32, name: 'Hedensted' },
|
|
69
|
+
{ id: 33, name: 'Sønderborg' },
|
|
70
|
+
{ id: 34, name: 'Greve' },
|
|
71
|
+
{ id: 35, name: 'Albertslund' },
|
|
72
|
+
{ id: 36, name: 'Skive' },
|
|
73
|
+
{ id: 37, name: 'Hørsholm' },
|
|
74
|
+
{ id: 38, name: 'Horsens' },
|
|
75
|
+
{ id: 39, name: 'Herning' },
|
|
76
|
+
{ id: 40, name: 'Frederiksberg' },
|
|
77
|
+
{ id: 41, name: 'Fredericia' },
|
|
78
|
+
{ id: 42, name: 'Roskilde' },
|
|
79
|
+
{ id: 43, name: 'Silkeborg' },
|
|
80
|
+
{ id: 44, name: 'Næstved' },
|
|
81
|
+
{ id: 45, name: 'Helsingør' },
|
|
82
|
+
{ id: 46, name: 'Hvidovre' },
|
|
83
|
+
{ id: 47, name: 'Holstebro' },
|
|
34
84
|
|
|
35
|
-
|
|
85
|
+
].filter((lang) => lang.name.toLowerCase().slice(0, inputLength) === inputValue);
|
|
36
86
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
{ id: 5, name: 'Esbjerg' },
|
|
45
|
-
{ id: 6, name: 'Randers' },
|
|
46
|
-
{ id: 7, name: 'Kolding' },
|
|
47
|
-
{ id: 8, name: 'Horsens' },
|
|
48
|
-
{ id: 9, name: 'Vejle' },
|
|
49
|
-
{ id: 10, name: 'Herning' },
|
|
50
|
-
{ id: 11, name: 'Frederiksberg' },
|
|
51
|
-
{ id: 12, name: 'Fredericia' },
|
|
52
|
-
{ id: 13, name: 'Roskilde' },
|
|
53
|
-
{ id: 14, name: 'Silkeborg' },
|
|
54
|
-
{ id: 15, name: 'Næstved' },
|
|
55
|
-
{ id: 16, name: 'Helsingør' },
|
|
56
|
-
{ id: 17, name: 'Hvidovre' },
|
|
57
|
-
{ id: 18, name: 'Holstebro' },
|
|
58
|
-
{ id: 19, name: 'Hjørring' },
|
|
59
|
-
{ id: 20, name: 'Glostrup' },
|
|
60
|
-
{ id: 21, name: 'Hillerød' },
|
|
61
|
-
{ id: 22, name: 'Slagelse' },
|
|
62
|
-
{ id: 23, name: 'Viborg' },
|
|
63
|
-
{ id: 24, name: 'Ikast-Brande' },
|
|
64
|
-
{ id: 25, name: 'Frederikshavn' },
|
|
65
|
-
{ id: 26, name: 'Ringsted' },
|
|
66
|
-
{ id: 27, name: 'Taastrup' },
|
|
67
|
-
{ id: 28, name: 'Køge' },
|
|
68
|
-
{ id: 29, name: 'Nørresundby' },
|
|
69
|
-
{ id: 30, name: 'Haderslev' },
|
|
70
|
-
{ id: 31, name: 'Varde' },
|
|
71
|
-
{ id: 32, name: 'Hedensted' },
|
|
72
|
-
{ id: 33, name: 'Sønderborg' },
|
|
73
|
-
{ id: 34, name: 'Greve' },
|
|
74
|
-
{ id: 35, name: 'Albertslund' },
|
|
75
|
-
{ id: 36, name: 'Skive' },
|
|
76
|
-
{ id: 37, name: 'Hørsholm' },
|
|
77
|
-
{ id: 38, name: 'Horsens' },
|
|
78
|
-
{ id: 39, name: 'Herning' },
|
|
79
|
-
{ id: 40, name: 'Frederiksberg' },
|
|
80
|
-
{ id: 41, name: 'Fredericia' },
|
|
81
|
-
{ id: 42, name: 'Roskilde' },
|
|
82
|
-
{ id: 43, name: 'Silkeborg' },
|
|
83
|
-
{ id: 44, name: 'Næstved' },
|
|
84
|
-
{ id: 45, name: 'Helsingør' },
|
|
85
|
-
{ id: 46, name: 'Hvidovre' },
|
|
86
|
-
{ id: 47, name: 'Holstebro' },
|
|
87
|
-
|
|
88
|
-
].filter((lang) => lang.name.toLowerCase().slice(0, inputLength) === inputValue);
|
|
89
|
-
|
|
90
|
-
callback(results);
|
|
87
|
+
callback(results);
|
|
88
|
+
},
|
|
89
|
+
getSuggestionValue: (suggestion) => suggestion.name,
|
|
90
|
+
setResults: () => { },
|
|
91
|
+
setQuery: () => { },
|
|
92
|
+
renderSuggestion: (suggestion) => <span>{suggestion.name}</span>,
|
|
93
|
+
placeholder: 'Search',
|
|
91
94
|
},
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
setQuery: () => { },
|
|
95
|
-
renderSuggestion: (suggestion) => <span>{suggestion.name}</span>,
|
|
96
|
-
placeholder: 'Search',
|
|
97
|
-
};
|
|
95
|
+
render: (args) => <AUAutoSuggestComponent {...args} />,
|
|
96
|
+
};
|