@axinom/mosaic-ui 0.55.0-rc.1 → 0.55.0-rc.11
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/DynamicDataList/helpers/useColumnDefs.d.ts +1 -1
- package/dist/components/DynamicDataList/helpers/useColumnDefs.d.ts.map +1 -1
- package/dist/components/FormElements/Select/Select.d.ts +15 -11
- package/dist/components/FormElements/Select/Select.d.ts.map +1 -1
- package/dist/components/FormElements/Select/SelectField.d.ts.map +1 -1
- package/dist/components/FormElements/SingleLineText/SingleLineText.d.ts.map +1 -1
- package/dist/components/FormElements/Tags/Tags.d.ts +2 -2
- package/dist/components/FormElements/Tags/Tags.d.ts.map +1 -1
- package/dist/components/FormElements/Tags/TagsField.d.ts +1 -1
- package/dist/components/FormElements/Tags/TagsField.d.ts.map +1 -1
- package/dist/index.es.js +4 -4
- package/dist/index.es.js.map +1 -1
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/package.json +4 -3
- package/src/components/DynamicDataList/DynamicDataList.spec.tsx +1 -5
- package/src/components/DynamicDataList/DynamicDataList.tsx +1 -1
- package/src/components/DynamicDataList/DynamicListDataEntry/Renderers/createSelectRenderer/createSelectRenderer.spec.tsx +9 -4
- package/src/components/DynamicDataList/DynamicListDataEntry/Renderers/createSelectRenderer/createSelectRenderer.tsx +2 -2
- package/src/components/DynamicDataList/helpers/useColumnDefs.ts +4 -2
- package/src/components/FormElements/FileUploadControl/FileUploadControl.scss +2 -3
- package/src/components/FormElements/FileUploadControl/FileUploadControl.stories.tsx +0 -1
- package/src/components/FormElements/FileUploadControl/FileUploadControl.tsx +2 -2
- package/src/components/FormElements/Select/Select.scss +108 -59
- package/src/components/FormElements/Select/Select.spec.tsx +52 -43
- package/src/components/FormElements/Select/Select.stories.tsx +8 -10
- package/src/components/FormElements/Select/Select.tsx +138 -52
- package/src/components/FormElements/Select/SelectField.tsx +1 -0
- package/src/components/FormElements/SingleLineText/SingleLineText.tsx +23 -7
- package/src/components/FormElements/Tags/Tags.scss +16 -76
- package/src/components/FormElements/Tags/Tags.spec.tsx +69 -80
- package/src/components/FormElements/Tags/Tags.tsx +54 -62
- package/src/components/FormElements/Tags/TagsField.tsx +3 -13
- package/src/styles/variables.scss +8 -0
|
@@ -1,73 +1,159 @@
|
|
|
1
|
+
import { Popper, useAutocomplete, UseAutocompleteProps } from '@mui/base';
|
|
1
2
|
import clsx from 'clsx';
|
|
2
|
-
import React from 'react';
|
|
3
|
-
import {
|
|
3
|
+
import React, { ChangeEvent } from 'react';
|
|
4
|
+
import { Button, ButtonContext } from '../../Buttons';
|
|
5
|
+
import { IconName } from '../../Icons';
|
|
6
|
+
import { BaseFormControl, BaseInputEvents } from '../Form.models';
|
|
4
7
|
import { FormElementContainer } from '../FormElementContainer';
|
|
5
8
|
import classes from './Select.scss';
|
|
6
9
|
|
|
7
|
-
export interface
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
export interface SelectOption {
|
|
11
|
+
value: string | number | string[];
|
|
12
|
+
label: string | number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
type TrimmedUseAutocompleteProps = Omit<
|
|
16
|
+
UseAutocompleteProps<SelectOption, false, true, false>,
|
|
17
|
+
'value' | 'onChange'
|
|
18
|
+
>;
|
|
19
|
+
export interface SelectProps
|
|
20
|
+
extends BaseFormControl,
|
|
21
|
+
BaseInputEvents,
|
|
22
|
+
TrimmedUseAutocompleteProps {
|
|
23
|
+
/** An array of options */
|
|
24
|
+
options: SelectOption[];
|
|
12
25
|
/** Whether or not the control should start focused (default: false) */
|
|
13
26
|
autoFocus?: boolean;
|
|
14
|
-
/**
|
|
15
|
-
|
|
27
|
+
/** Current value the form control has */
|
|
28
|
+
value?: string | string[] | number;
|
|
16
29
|
/** Select placeholder */
|
|
17
30
|
placeholder?: string;
|
|
31
|
+
/** Allows to clear the input field and leave empty */
|
|
32
|
+
addEmptyOption?: boolean;
|
|
18
33
|
}
|
|
19
34
|
|
|
20
|
-
export const Select: React.FC<SelectProps> = ({
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
export const Select: React.FC<SelectProps> = (props) => {
|
|
36
|
+
const {
|
|
37
|
+
name,
|
|
38
|
+
error,
|
|
39
|
+
disabled,
|
|
40
|
+
id,
|
|
41
|
+
label,
|
|
42
|
+
className,
|
|
43
|
+
tooltipContent,
|
|
44
|
+
inlineMode,
|
|
45
|
+
autoFocus,
|
|
46
|
+
value,
|
|
47
|
+
onBlur,
|
|
48
|
+
onFocus,
|
|
49
|
+
onChange,
|
|
50
|
+
placeholder,
|
|
51
|
+
addEmptyOption,
|
|
52
|
+
...autoCompleteProps
|
|
53
|
+
} = props;
|
|
54
|
+
const {
|
|
55
|
+
getRootProps,
|
|
56
|
+
getInputProps,
|
|
57
|
+
getListboxProps,
|
|
58
|
+
getOptionProps,
|
|
59
|
+
getPopupIndicatorProps,
|
|
60
|
+
groupedOptions,
|
|
61
|
+
popupOpen,
|
|
62
|
+
setAnchorEl,
|
|
63
|
+
anchorEl,
|
|
64
|
+
} = useAutocomplete({
|
|
65
|
+
id,
|
|
66
|
+
disabled,
|
|
67
|
+
value:
|
|
68
|
+
autoCompleteProps.options.find((option) => option.value === value) ??
|
|
69
|
+
null,
|
|
70
|
+
onChange: ({ target: _, ...event }, value) => {
|
|
71
|
+
onChange?.({
|
|
72
|
+
...event,
|
|
73
|
+
currentTarget: {
|
|
74
|
+
...event.currentTarget,
|
|
75
|
+
id,
|
|
76
|
+
name,
|
|
77
|
+
value: value?.value ?? '',
|
|
78
|
+
},
|
|
79
|
+
} as ChangeEvent<HTMLInputElement>);
|
|
80
|
+
},
|
|
81
|
+
disableClearable: !addEmptyOption,
|
|
82
|
+
...autoCompleteProps,
|
|
83
|
+
});
|
|
37
84
|
|
|
38
85
|
return (
|
|
39
86
|
<FormElementContainer
|
|
40
|
-
{
|
|
87
|
+
id={id}
|
|
88
|
+
label={label}
|
|
89
|
+
tooltipContent={tooltipContent}
|
|
90
|
+
inlineMode={inlineMode}
|
|
41
91
|
className={clsx(classes.container, 'select-container', className)}
|
|
42
|
-
error={
|
|
92
|
+
error={error}
|
|
43
93
|
dataTestFieldType="Select"
|
|
44
94
|
>
|
|
45
|
-
<
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
value={value}
|
|
50
|
-
disabled={disabled}
|
|
51
|
-
autoFocus={autoFocus}
|
|
52
|
-
onChange={onChange}
|
|
53
|
-
onBlur={onBlur}
|
|
54
|
-
onFocus={onFocus}
|
|
95
|
+
<div
|
|
96
|
+
ref={setAnchorEl}
|
|
97
|
+
{...getRootProps()}
|
|
98
|
+
className={clsx(classes.inputWrapper)}
|
|
55
99
|
>
|
|
56
|
-
|
|
57
|
-
(
|
|
58
|
-
|
|
100
|
+
<input
|
|
101
|
+
{...getInputProps()}
|
|
102
|
+
name={name}
|
|
103
|
+
className={clsx({ [classes.hasError]: Boolean(error) })}
|
|
104
|
+
autoFocus={autoFocus}
|
|
105
|
+
onBlur={(event) => {
|
|
106
|
+
getInputProps().onBlur?.(event);
|
|
107
|
+
onBlur?.(event);
|
|
108
|
+
}}
|
|
109
|
+
onFocus={(event) => {
|
|
110
|
+
getInputProps().onFocus?.(event);
|
|
111
|
+
onFocus?.(event);
|
|
112
|
+
}}
|
|
113
|
+
placeholder={placeholder}
|
|
114
|
+
/>
|
|
115
|
+
<Button
|
|
116
|
+
className={clsx(classes.button)}
|
|
117
|
+
buttonContext={ButtonContext.None}
|
|
118
|
+
icon={popupOpen ? IconName.ChevronUp : IconName.ChevronDown}
|
|
119
|
+
onButtonClicked={getPopupIndicatorProps().onClick}
|
|
120
|
+
onBlur={getPopupIndicatorProps().onBlur}
|
|
121
|
+
disabled={disabled}
|
|
122
|
+
/>
|
|
123
|
+
</div>
|
|
124
|
+
<Popper
|
|
125
|
+
open={popupOpen}
|
|
126
|
+
anchorEl={anchorEl}
|
|
127
|
+
style={{
|
|
128
|
+
width: anchorEl ? anchorEl.clientWidth : undefined,
|
|
129
|
+
zIndex: 999,
|
|
130
|
+
}}
|
|
131
|
+
>
|
|
132
|
+
<PopperContent>
|
|
133
|
+
{groupedOptions.length > 0 && (
|
|
134
|
+
<ul {...getListboxProps()}>
|
|
135
|
+
{(groupedOptions as typeof autoCompleteProps.options).map(
|
|
136
|
+
(option, index) => (
|
|
137
|
+
<li
|
|
138
|
+
key={String(option.value)}
|
|
139
|
+
{...getOptionProps({ option, index })}
|
|
140
|
+
>
|
|
141
|
+
{option.label}
|
|
142
|
+
</li>
|
|
143
|
+
),
|
|
144
|
+
)}
|
|
145
|
+
</ul>
|
|
59
146
|
)}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
{disabled ? '' : placeholder}
|
|
63
|
-
</option>
|
|
64
|
-
)}
|
|
65
|
-
{options.map((option) => (
|
|
66
|
-
<option key={option.value} value={option.value}>
|
|
67
|
-
{option.label}
|
|
68
|
-
</option>
|
|
69
|
-
))}
|
|
70
|
-
</select>
|
|
147
|
+
</PopperContent>
|
|
148
|
+
</Popper>
|
|
71
149
|
</FormElementContainer>
|
|
72
150
|
);
|
|
73
151
|
};
|
|
152
|
+
|
|
153
|
+
const PopperContent: React.FC = ({ children }) => {
|
|
154
|
+
return (
|
|
155
|
+
<div className={clsx(classes.popperContent, 'select-popper-content')}>
|
|
156
|
+
{children}
|
|
157
|
+
</div>
|
|
158
|
+
);
|
|
159
|
+
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { useFormikError } from '../useFormikError';
|
|
3
3
|
import { Select, SelectProps } from './Select';
|
|
4
|
+
|
|
4
5
|
export const SelectField: React.FC<Omit<SelectProps, 'error'>> = (props) => {
|
|
5
6
|
const error = useFormikError(props.name);
|
|
6
7
|
return <Select {...props} error={error} />;
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import clsx from 'clsx';
|
|
2
|
-
import React, {
|
|
2
|
+
import React, {
|
|
3
|
+
ChangeEvent,
|
|
4
|
+
InputHTMLAttributes,
|
|
5
|
+
useEffect,
|
|
6
|
+
useRef,
|
|
7
|
+
} from 'react';
|
|
3
8
|
import { executeIfRefAvailable } from '../../../helpers/utils';
|
|
4
9
|
import { BaseFormControl, BaseInputEvents } from '../Form.models';
|
|
5
10
|
import { FormElementContainer } from '../FormElementContainer';
|
|
@@ -50,7 +55,7 @@ export const SingleLineText: React.FC<SingleLineTextProps> = ({
|
|
|
50
55
|
error,
|
|
51
56
|
autoFocus = false,
|
|
52
57
|
autoComplete,
|
|
53
|
-
innerRef
|
|
58
|
+
innerRef: refFromParent,
|
|
54
59
|
defaultValue,
|
|
55
60
|
onChange,
|
|
56
61
|
onBlur,
|
|
@@ -59,9 +64,13 @@ export const SingleLineText: React.FC<SingleLineTextProps> = ({
|
|
|
59
64
|
inputProps,
|
|
60
65
|
...rest
|
|
61
66
|
}) => {
|
|
67
|
+
// Create a local ref if no ref is passed from the parent
|
|
68
|
+
const localRef = useRef<HTMLInputElement>(null);
|
|
69
|
+
const innerRef = refFromParent || localRef;
|
|
70
|
+
|
|
62
71
|
const errorMsg: string | undefined = error;
|
|
63
72
|
const DUMMY_PWD = '0000000000';
|
|
64
|
-
const isPasswordField = type === 'password'
|
|
73
|
+
const isPasswordField = type === 'password';
|
|
65
74
|
|
|
66
75
|
useEffect(() => {
|
|
67
76
|
if (innerRef.current) {
|
|
@@ -70,15 +79,22 @@ export const SingleLineText: React.FC<SingleLineTextProps> = ({
|
|
|
70
79
|
}, [innerRef, value]);
|
|
71
80
|
|
|
72
81
|
useEffect(() => {
|
|
73
|
-
if
|
|
82
|
+
// Only set the dummy password if the field is a password field and the value is not set
|
|
83
|
+
// and the field is not focused
|
|
84
|
+
if (
|
|
85
|
+
isPasswordField &&
|
|
86
|
+
isSet &&
|
|
87
|
+
!value &&
|
|
88
|
+
document.activeElement !== innerRef.current
|
|
89
|
+
) {
|
|
74
90
|
executeIfRefAvailable(innerRef, (input) => {
|
|
75
91
|
input.value = DUMMY_PWD;
|
|
76
92
|
});
|
|
77
93
|
}
|
|
78
|
-
}, [innerRef, isPasswordField, isSet]);
|
|
94
|
+
}, [innerRef, isPasswordField, isSet, value]);
|
|
79
95
|
|
|
80
96
|
const onFocusWrapper = (e: ChangeEvent<HTMLInputElement>): void => {
|
|
81
|
-
if (
|
|
97
|
+
if (isPasswordField && isSet && !value) {
|
|
82
98
|
executeIfRefAvailable(innerRef, (input) => {
|
|
83
99
|
input.value = '';
|
|
84
100
|
});
|
|
@@ -86,7 +102,7 @@ export const SingleLineText: React.FC<SingleLineTextProps> = ({
|
|
|
86
102
|
onFocus?.(e);
|
|
87
103
|
};
|
|
88
104
|
const onBlurWrapper = (e: ChangeEvent<HTMLInputElement>): void => {
|
|
89
|
-
if (
|
|
105
|
+
if (isPasswordField && isSet && !value) {
|
|
90
106
|
executeIfRefAvailable(innerRef, (input) => {
|
|
91
107
|
input.value = DUMMY_PWD;
|
|
92
108
|
});
|
|
@@ -10,6 +10,22 @@
|
|
|
10
10
|
flex-flow: wrap;
|
|
11
11
|
gap: 10px;
|
|
12
12
|
|
|
13
|
+
.hasError {
|
|
14
|
+
input {
|
|
15
|
+
border: 1px solid
|
|
16
|
+
var(--input-invalid-border-color, $input-invalid-border-color);
|
|
17
|
+
|
|
18
|
+
&:hover {
|
|
19
|
+
border-color: var(
|
|
20
|
+
--input-invalid-border-color,
|
|
21
|
+
$input-invalid-border-color
|
|
22
|
+
);
|
|
23
|
+
box-shadow: 0 0 0 2px
|
|
24
|
+
var(--input-invalid-hover-color, $input-invalid-hover-color);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
13
29
|
.selectedItem {
|
|
14
30
|
width: max-content;
|
|
15
31
|
display: grid;
|
|
@@ -38,82 +54,6 @@
|
|
|
38
54
|
display: block;
|
|
39
55
|
grid-template-columns: none;
|
|
40
56
|
}
|
|
41
|
-
|
|
42
|
-
select {
|
|
43
|
-
-moz-appearance: none;
|
|
44
|
-
-webkit-appearance: none;
|
|
45
|
-
appearance: none;
|
|
46
|
-
|
|
47
|
-
color: var(--input-color, $input-color);
|
|
48
|
-
border: 1px solid var(--input-border-color, $input-border-color);
|
|
49
|
-
|
|
50
|
-
font-size: var(--label-font-size, $label-font-size);
|
|
51
|
-
|
|
52
|
-
height: 50px;
|
|
53
|
-
max-width: $tags-max-width;
|
|
54
|
-
|
|
55
|
-
padding: 0 40px 0 12px;
|
|
56
|
-
|
|
57
|
-
// CSS variables will not work inside the background-image url - we still pass it in there for consistency reasons.
|
|
58
|
-
background-image: svg-arrow-glyph(
|
|
59
|
-
var(--select-arrow-color, encodecolor($select-arrow-color))
|
|
60
|
-
);
|
|
61
|
-
background-repeat: no-repeat;
|
|
62
|
-
background-position-y: center;
|
|
63
|
-
background-position-x: 100%;
|
|
64
|
-
|
|
65
|
-
background-color: var(
|
|
66
|
-
--select-background-color,
|
|
67
|
-
$select-background-color
|
|
68
|
-
);
|
|
69
|
-
|
|
70
|
-
cursor: pointer;
|
|
71
|
-
outline: none;
|
|
72
|
-
|
|
73
|
-
transition: box-shadow 0.15s ease-in-out 0s;
|
|
74
|
-
|
|
75
|
-
&.hasError {
|
|
76
|
-
border: 1px solid
|
|
77
|
-
var(--input-invalid-border-color, $input-invalid-border-color);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
&:disabled {
|
|
81
|
-
border-color: var(
|
|
82
|
-
--input-disabled-border-color,
|
|
83
|
-
$input-disabled-border-color
|
|
84
|
-
);
|
|
85
|
-
|
|
86
|
-
cursor: default;
|
|
87
|
-
|
|
88
|
-
background-image: svg-arrow-glyph(
|
|
89
|
-
var(--select-arrow-color, encodecolor($select-disabled-arrow-color))
|
|
90
|
-
);
|
|
91
|
-
background-color: var(
|
|
92
|
-
--input-disabled-background-color,
|
|
93
|
-
$input-disabled-background-color
|
|
94
|
-
);
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
select,
|
|
99
|
-
option {
|
|
100
|
-
width: 325px;
|
|
101
|
-
overflow: hidden;
|
|
102
|
-
white-space: nowrap;
|
|
103
|
-
text-overflow: ellipsis;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
select:hover:enabled {
|
|
107
|
-
border: 1px solid var(--input-hover-color, $input-hover-color);
|
|
108
|
-
box-shadow: 0 0 0 2px var(--input-hover-color, $input-hover-color);
|
|
109
|
-
|
|
110
|
-
&.hasError {
|
|
111
|
-
border: 1px solid
|
|
112
|
-
var(--input-invalid-border-color, $input-invalid-border-color);
|
|
113
|
-
box-shadow: 0 0 0 2px
|
|
114
|
-
var(--input-invalid-hover-color, $input-invalid-hover-color);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
57
|
}
|
|
118
58
|
}
|
|
119
59
|
|
|
@@ -1,9 +1,16 @@
|
|
|
1
|
-
import { mount, shallow } from 'enzyme';
|
|
1
|
+
import { mount, ReactWrapper, shallow } from 'enzyme';
|
|
2
2
|
import React from 'react';
|
|
3
|
-
import { act } from 'react-dom/test-utils';
|
|
4
3
|
import { Button } from '../../Buttons';
|
|
4
|
+
import { Select } from '../Select/Select';
|
|
5
5
|
import { Tags } from './Tags';
|
|
6
6
|
|
|
7
|
+
function selectFirstOption(wrapper: ReactWrapper) {
|
|
8
|
+
const input = wrapper.find('[role="combobox"]');
|
|
9
|
+
input.simulate('mousedown');
|
|
10
|
+
const firstOption = document.querySelector('[role="option"]');
|
|
11
|
+
firstOption?.dispatchEvent(new Event('click', { bubbles: true }));
|
|
12
|
+
}
|
|
13
|
+
|
|
7
14
|
describe('Tags', () => {
|
|
8
15
|
it('renders the component without crashing', () => {
|
|
9
16
|
const wrapper = shallow(<Tags name={'test-name'} />);
|
|
@@ -41,61 +48,54 @@ describe('Tags', () => {
|
|
|
41
48
|
});
|
|
42
49
|
});
|
|
43
50
|
|
|
44
|
-
it('raises onChange when selecting a tag along with the new value',
|
|
45
|
-
const
|
|
51
|
+
it('raises onChange when selecting a tag along with the new value', () => {
|
|
52
|
+
const changeSpy = jest.fn();
|
|
46
53
|
const mockValue: string[] = ['1'];
|
|
47
|
-
const mockValueUpdated = '2';
|
|
48
54
|
const wrapper = mount(
|
|
49
|
-
<Tags
|
|
55
|
+
<Tags
|
|
56
|
+
name={'test-name'}
|
|
57
|
+
onChange={changeSpy}
|
|
58
|
+
value={mockValue}
|
|
59
|
+
tagsOptions={['1', '2']}
|
|
60
|
+
/>,
|
|
50
61
|
);
|
|
51
62
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
await act(async () => {
|
|
55
|
-
await select.simulate('change', {
|
|
56
|
-
currentTarget: { value: mockValueUpdated },
|
|
57
|
-
persist: jest.fn(),
|
|
58
|
-
});
|
|
59
|
-
wrapper.update();
|
|
60
|
-
});
|
|
63
|
+
selectFirstOption(wrapper);
|
|
61
64
|
|
|
62
|
-
expect(
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
// });
|
|
65
|
+
expect(changeSpy).toHaveBeenCalledTimes(1);
|
|
66
|
+
expect(changeSpy).toHaveBeenCalledWith(
|
|
67
|
+
expect.objectContaining({
|
|
68
|
+
currentTarget: expect.objectContaining({ value: ['1', '2'] }),
|
|
69
|
+
}),
|
|
70
|
+
);
|
|
69
71
|
});
|
|
70
72
|
|
|
71
|
-
it('raises onChange when removing a tag along with the new value',
|
|
72
|
-
const
|
|
73
|
+
it('raises onChange when removing a tag along with the new value', () => {
|
|
74
|
+
const changeSpy = jest.fn();
|
|
73
75
|
const mockValue: string[] = ['1'];
|
|
74
76
|
const wrapper = mount(
|
|
75
77
|
<Tags
|
|
76
78
|
name={'test-name'}
|
|
77
|
-
onChange={
|
|
79
|
+
onChange={changeSpy}
|
|
78
80
|
value={mockValue}
|
|
79
81
|
tagsOptions={['1', '2']}
|
|
80
82
|
/>,
|
|
81
83
|
);
|
|
82
84
|
|
|
83
|
-
const x = wrapper.find('
|
|
85
|
+
const x = wrapper.find('[data-test-id="tags-delete"]');
|
|
84
86
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
persist: jest.fn(),
|
|
88
|
-
});
|
|
89
|
-
wrapper.update();
|
|
87
|
+
x.simulate('click', {
|
|
88
|
+
persist: jest.fn(),
|
|
90
89
|
});
|
|
91
90
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
91
|
+
wrapper.update();
|
|
92
|
+
|
|
93
|
+
expect(changeSpy).toHaveBeenCalledTimes(1);
|
|
94
|
+
expect(changeSpy).toHaveBeenCalledWith(
|
|
95
|
+
expect.objectContaining({
|
|
96
|
+
currentTarget: expect.objectContaining({ value: [] }),
|
|
97
|
+
}),
|
|
98
|
+
);
|
|
99
99
|
});
|
|
100
100
|
|
|
101
101
|
it('shows select element when current selected tags and optional tags are the not same length', () => {
|
|
@@ -103,37 +103,38 @@ describe('Tags', () => {
|
|
|
103
103
|
<Tags name={'test-name'} value={['1']} tagsOptions={['1', '2']} />,
|
|
104
104
|
);
|
|
105
105
|
|
|
106
|
-
const select = wrapper.find(
|
|
106
|
+
const select = wrapper.find(Select);
|
|
107
107
|
|
|
108
|
-
expect(select
|
|
108
|
+
expect(select).toHaveLength(1);
|
|
109
109
|
});
|
|
110
110
|
|
|
111
111
|
it('hides select element when current selected tags and optional tags are the same length', () => {
|
|
112
|
-
const wrapper =
|
|
112
|
+
const wrapper = mount(
|
|
113
113
|
<Tags name={'test-name'} value={['1']} tagsOptions={['1', '2']} />,
|
|
114
114
|
);
|
|
115
115
|
|
|
116
|
-
|
|
116
|
+
selectFirstOption(wrapper);
|
|
117
117
|
|
|
118
|
-
|
|
118
|
+
wrapper.update();
|
|
119
119
|
|
|
120
|
-
select.
|
|
121
|
-
currentTarget: { value: '2' },
|
|
122
|
-
persist: jest.fn(),
|
|
123
|
-
});
|
|
124
|
-
select = wrapper.find('select');
|
|
120
|
+
const select = wrapper.find(Select);
|
|
125
121
|
|
|
126
|
-
expect(select
|
|
122
|
+
expect(select).toHaveLength(0);
|
|
127
123
|
});
|
|
128
124
|
|
|
129
125
|
it('raises blur and focus events', () => {
|
|
130
126
|
const blurSpy = jest.fn();
|
|
131
127
|
const focusSpy = jest.fn();
|
|
132
128
|
const wrapper = shallow(
|
|
133
|
-
<Tags
|
|
129
|
+
<Tags
|
|
130
|
+
name={'test-name'}
|
|
131
|
+
onBlur={blurSpy}
|
|
132
|
+
onFocus={focusSpy}
|
|
133
|
+
tagsOptions={['1']}
|
|
134
|
+
/>,
|
|
134
135
|
);
|
|
135
136
|
|
|
136
|
-
const select = wrapper.find(
|
|
137
|
+
const select = wrapper.find(Select);
|
|
137
138
|
|
|
138
139
|
select.simulate('blur');
|
|
139
140
|
expect(blurSpy).toHaveBeenCalledTimes(1);
|
|
@@ -166,7 +167,7 @@ describe('Tags', () => {
|
|
|
166
167
|
});
|
|
167
168
|
|
|
168
169
|
it('uses the displayValue for available tags when tagOptions are objects', () => {
|
|
169
|
-
const wrapper =
|
|
170
|
+
const wrapper = mount(
|
|
170
171
|
<Tags
|
|
171
172
|
name={'test-name'}
|
|
172
173
|
value={['1', '3']}
|
|
@@ -181,43 +182,31 @@ describe('Tags', () => {
|
|
|
181
182
|
/>,
|
|
182
183
|
);
|
|
183
184
|
|
|
184
|
-
const
|
|
185
|
+
const input = wrapper.find('[role="combobox"]');
|
|
186
|
+
input.simulate('mousedown');
|
|
185
187
|
|
|
186
|
-
|
|
188
|
+
const popper = document.querySelector('[role="tooltip"]');
|
|
189
|
+
|
|
190
|
+
const options = popper?.querySelectorAll('[role="option"]');
|
|
191
|
+
|
|
192
|
+
expect(options).toHaveLength(2);
|
|
187
193
|
|
|
188
|
-
expect(
|
|
194
|
+
expect([options?.[0].innerHTML, options?.[1].innerHTML]).toEqual([
|
|
195
|
+
'Test2',
|
|
196
|
+
'Test4',
|
|
197
|
+
]);
|
|
189
198
|
});
|
|
190
199
|
|
|
191
200
|
it('applies error styling and renders error message when an error is passed', () => {
|
|
192
201
|
const mockErrorMessage = 'test-error-message';
|
|
193
|
-
const wrapper =
|
|
194
|
-
<Tags name={'test-name'} error={mockErrorMessage} />,
|
|
202
|
+
const wrapper = mount(
|
|
203
|
+
<Tags name={'test-name'} error={mockErrorMessage} tagsOptions={['1']} />,
|
|
195
204
|
);
|
|
196
205
|
|
|
197
|
-
const errorMsg = wrapper.
|
|
198
|
-
const
|
|
206
|
+
const errorMsg = wrapper.find('small');
|
|
207
|
+
const select = wrapper.find(Select);
|
|
199
208
|
|
|
200
209
|
expect(errorMsg.text()).toBe(mockErrorMessage);
|
|
201
|
-
expect(
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
it('defaults drop down label and value to empty strings', () => {
|
|
205
|
-
const wrapper = shallow(<Tags name={'test-name'} />);
|
|
206
|
-
|
|
207
|
-
const options = wrapper.find('option');
|
|
208
|
-
|
|
209
|
-
expect(options.at(0).text()).toBe('');
|
|
210
|
-
expect(options.at(0).prop('value')).toBe('');
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
it('displays a label for the drop down', () => {
|
|
214
|
-
const mockDropDownLabel = 'mockLabel';
|
|
215
|
-
const wrapper = shallow(
|
|
216
|
-
<Tags name={'test-name'} dropDownLabel={mockDropDownLabel} />,
|
|
217
|
-
);
|
|
218
|
-
|
|
219
|
-
const options = wrapper.find('option');
|
|
220
|
-
|
|
221
|
-
expect(options.at(0).text()).toBe(mockDropDownLabel);
|
|
210
|
+
expect(select.hasClass('hasError')).toBe(true);
|
|
222
211
|
});
|
|
223
212
|
});
|