@axinom/mosaic-ui 0.54.0-rc.1 → 0.54.0-rc.2
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/DynamicListDataEntry/Renderers/createSelectRenderer/createSelectRenderer.d.ts.map +1 -1
- package/dist/components/FormElements/Select/Select.d.ts +2 -0
- package/dist/components/FormElements/Select/Select.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 +3 -3
- package/src/components/DynamicDataList/DynamicDataList.stories.tsx +4 -0
- package/src/components/DynamicDataList/DynamicListDataEntry/Renderers/createSelectRenderer/createSelectRenderer.scss +2 -79
- package/src/components/DynamicDataList/DynamicListDataEntry/Renderers/createSelectRenderer/createSelectRenderer.spec.tsx +17 -85
- package/src/components/DynamicDataList/DynamicListDataEntry/Renderers/createSelectRenderer/createSelectRenderer.tsx +17 -28
- package/src/components/FormElements/Select/Select.tsx +12 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@axinom/mosaic-ui",
|
|
3
|
-
"version": "0.54.0-rc.
|
|
3
|
+
"version": "0.54.0-rc.2",
|
|
4
4
|
"description": "UI components for building Axinom Mosaic applications",
|
|
5
5
|
"author": "Axinom",
|
|
6
6
|
"license": "PROPRIETARY",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"build-storybook": "storybook build"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@axinom/mosaic-core": "^0.4.27-rc.
|
|
35
|
+
"@axinom/mosaic-core": "^0.4.27-rc.2",
|
|
36
36
|
"@faker-js/faker": "^7.4.0",
|
|
37
37
|
"@geoffcox/react-splitter": "^2.1.2",
|
|
38
38
|
"@popperjs/core": "^2.11.8",
|
|
@@ -106,5 +106,5 @@
|
|
|
106
106
|
"publishConfig": {
|
|
107
107
|
"access": "public"
|
|
108
108
|
},
|
|
109
|
-
"gitHead": "
|
|
109
|
+
"gitHead": "2d015f129e61beade8826336c6d32e49a5eb36a3"
|
|
110
110
|
}
|
|
@@ -246,6 +246,10 @@ export const WithSelectInputRenderer: StoryObj<StoryDDLType> = {
|
|
|
246
246
|
},
|
|
247
247
|
],
|
|
248
248
|
allowNewData: true,
|
|
249
|
+
rowValidationSchema: Yup.object({
|
|
250
|
+
id: Yup.number().required(),
|
|
251
|
+
title: Yup.string().required().max(25).label('Title'),
|
|
252
|
+
}),
|
|
249
253
|
},
|
|
250
254
|
};
|
|
251
255
|
|
|
@@ -1,92 +1,15 @@
|
|
|
1
1
|
@import '../../../../../styles/common.scss';
|
|
2
2
|
|
|
3
|
-
@function svg-arrow-glyph($color) {
|
|
4
|
-
@return url('data:image/svg+xml;utf8,<svg stroke="' + $color + '" version="1.1" xmlns="http://www.w3.org/2000/svg" width="40px" height="20px" viewBox="0 0 40 40"><path vector-effect="non-scaling-stroke" fill="none" stroke-width="2" d="M38.5,9.5L20,30.5L1.5,9.5" /></svg>');
|
|
5
|
-
}
|
|
6
|
-
|
|
7
3
|
.container {
|
|
8
4
|
height: 100%;
|
|
9
5
|
width: 100%;
|
|
10
6
|
|
|
11
7
|
display: grid;
|
|
12
8
|
grid-template-columns: 1fr;
|
|
13
|
-
grid-template-rows:
|
|
9
|
+
grid-template-rows: min-content min-content;
|
|
14
10
|
|
|
15
11
|
select {
|
|
16
|
-
|
|
17
|
-
height: var(--dynamic-list-input-height, $dynamic-list-input-height);
|
|
12
|
+
max-width: none;
|
|
18
13
|
width: 100%;
|
|
19
|
-
|
|
20
|
-
padding-left: 6px;
|
|
21
|
-
|
|
22
|
-
color: var(--input-color, $input-color);
|
|
23
|
-
font-size: var(--label-font-size, $label-font-size);
|
|
24
|
-
|
|
25
|
-
-moz-appearance: none;
|
|
26
|
-
-webkit-appearance: none;
|
|
27
|
-
appearance: none;
|
|
28
|
-
cursor: pointer;
|
|
29
|
-
color: var(--input-color, $input-color);
|
|
30
|
-
border: 1px solid var(--input-border-color, $input-border-color);
|
|
31
|
-
outline: none;
|
|
32
|
-
|
|
33
|
-
background-image: svg-arrow-glyph(
|
|
34
|
-
var(--select-arrow-color, encodecolor($select-arrow-color))
|
|
35
|
-
);
|
|
36
|
-
//background-image: url('data:image/svg+xml;utf8,<svg stroke="%231478af" version="1.1" xmlns="http://www.w3.org/2000/svg" width="40px" height="20px" viewBox="0 0 40 40"><path vector-effect="non-scaling-stroke" fill="none" stroke-width="2" d="M38.5,9.5L20,30.5L1.5,9.5" /></svg>');
|
|
37
|
-
background-repeat: no-repeat;
|
|
38
|
-
background-position: center;
|
|
39
|
-
background-position-x: 100%;
|
|
40
|
-
|
|
41
|
-
padding: 0 40px 0 12px;
|
|
42
|
-
|
|
43
|
-
transition: box-shadow 0.15s ease-in-out 0s;
|
|
44
|
-
|
|
45
|
-
&.hasError {
|
|
46
|
-
border: 1px solid
|
|
47
|
-
var(--input-invalid-border-color, $input-invalid-border-color);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
&:disabled {
|
|
51
|
-
border-color: var(
|
|
52
|
-
--input-disabled-border-color,
|
|
53
|
-
$input-disabled-border-color
|
|
54
|
-
);
|
|
55
|
-
|
|
56
|
-
cursor: default;
|
|
57
|
-
|
|
58
|
-
background-image: svg-arrow-glyph(
|
|
59
|
-
var(--select-arrow-color, encodecolor($select-disabled-arrow-color))
|
|
60
|
-
);
|
|
61
|
-
background-color: var(
|
|
62
|
-
--input-disabled-background-color,
|
|
63
|
-
$input-disabled-background-color
|
|
64
|
-
);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
&:not(:disabled):hover {
|
|
68
|
-
border: 1px solid var(--input-hover-color, $input-hover-color);
|
|
69
|
-
box-shadow: 0 0 0 2px var(--input-hover-color, $input-hover-color);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
&.hasError {
|
|
73
|
-
border: 1px solid
|
|
74
|
-
var(--input-invalid-border-color, $input-invalid-border-color);
|
|
75
|
-
box-shadow: 0 0 0 2px
|
|
76
|
-
var(--input-invalid-hover-color, $input-invalid-hover-color);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
select,
|
|
81
|
-
option {
|
|
82
|
-
overflow: hidden;
|
|
83
|
-
white-space: nowrap;
|
|
84
|
-
text-overflow: ellipsis;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
select > small {
|
|
88
|
-
padding: 6px 0;
|
|
89
|
-
white-space: normal;
|
|
90
|
-
color: var(--input-invalid-color, $input-invalid-color);
|
|
91
14
|
}
|
|
92
15
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { shallow } from 'enzyme';
|
|
1
|
+
import { mount, shallow } from 'enzyme';
|
|
2
2
|
import React from 'react';
|
|
3
3
|
import { act } from 'react-dom/test-utils';
|
|
4
4
|
import { noop } from '../../../../../helpers/utils';
|
|
5
|
+
import { Select } from '../../../../FormElements';
|
|
5
6
|
import { CreateSelectRendererConfig } from '../renderers.model';
|
|
6
7
|
import { createSelectRenderer } from './createSelectRenderer';
|
|
7
8
|
|
|
@@ -42,102 +43,29 @@ describe('createSelectRenderer', () => {
|
|
|
42
43
|
expect(wrapper).toBeTruthy();
|
|
43
44
|
});
|
|
44
45
|
|
|
45
|
-
it(`renders an 'id' attribute on the 'select' element if set`, () => {
|
|
46
|
-
const mockId = 'test-id';
|
|
47
|
-
const wrapper = shallow(<RendererWrapper id={mockId} />);
|
|
48
|
-
|
|
49
|
-
const select = wrapper.find('select');
|
|
50
|
-
|
|
51
|
-
expect(select.prop('id')).toBe(mockId);
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
it(`renders a 'name' attribute on the 'select' element if set`, () => {
|
|
55
|
-
const mockName = 'test-name';
|
|
56
|
-
const wrapper = shallow(<RendererWrapper name={mockName} />);
|
|
57
|
-
|
|
58
|
-
const select = wrapper.find('select');
|
|
59
|
-
|
|
60
|
-
expect(select.prop('name')).toBe(mockName);
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it('renders "disabled" attribute on the "select" element with value "false" if not set otherwise', () => {
|
|
64
|
-
const wrapper = shallow(<RendererWrapper />);
|
|
65
|
-
|
|
66
|
-
const input = wrapper.find('select');
|
|
67
|
-
|
|
68
|
-
expect(input.prop('disabled')).toBe(false);
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
it('renders "disabled" attribute on the "select" element if set', () => {
|
|
72
|
-
const wrapper = shallow(<RendererWrapper disabled={true} />);
|
|
73
|
-
|
|
74
|
-
const input = wrapper.find('select');
|
|
75
|
-
|
|
76
|
-
expect(input.prop('disabled')).toBe(true);
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
it(`doesn't render placeholder by default`, () => {
|
|
80
|
-
const wrapper = shallow(<RendererWrapper />);
|
|
81
|
-
|
|
82
|
-
const option = wrapper.find('option');
|
|
83
|
-
|
|
84
|
-
expect(option.exists()).toBe(false);
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
it(`renders a placeholder option if the 'placeholder' prop is set`, () => {
|
|
88
|
-
const mockPlaceholder = 'test-placeholder';
|
|
89
|
-
const wrapper = shallow(<RendererWrapper placeholder={mockPlaceholder} />);
|
|
90
|
-
|
|
91
|
-
const option = wrapper.find('option');
|
|
92
|
-
|
|
93
|
-
expect(option.text()).toBe(mockPlaceholder);
|
|
94
|
-
expect(option.prop('value')).toBe('');
|
|
95
|
-
expect(option.prop('disabled')).toBe(true);
|
|
96
|
-
});
|
|
97
46
|
it(`sets 'select' value to current value`, () => {
|
|
98
47
|
const mockValue = 'test-value';
|
|
99
|
-
const wrapper =
|
|
48
|
+
const wrapper = mount(<RendererWrapper currentValue={mockValue} />);
|
|
100
49
|
|
|
101
50
|
const select = wrapper.find('select');
|
|
102
51
|
|
|
103
52
|
expect(select.prop('value')).toBe(mockValue);
|
|
104
53
|
});
|
|
105
54
|
|
|
106
|
-
it('renders all options', () => {
|
|
107
|
-
const mockOptions = [
|
|
108
|
-
{ value: '10', label: '10' },
|
|
109
|
-
{ value: '11', label: '11' },
|
|
110
|
-
];
|
|
111
|
-
const wrapper = shallow(<RendererWrapper options={mockOptions} />);
|
|
112
|
-
|
|
113
|
-
const options = wrapper.find('option');
|
|
114
|
-
|
|
115
|
-
options.forEach((option, idx) => {
|
|
116
|
-
expect(option.text()).toBe(mockOptions[idx].label);
|
|
117
|
-
expect(option.prop('value')).toBe(mockOptions[idx].value);
|
|
118
|
-
});
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
it(`doesn't render any option elements if 'options' prop is not set`, () => {
|
|
122
|
-
const wrapper = shallow(<RendererWrapper options={undefined} />);
|
|
123
|
-
|
|
124
|
-
const options = wrapper.find('option');
|
|
125
|
-
|
|
126
|
-
expect(options.exists()).toBe(false);
|
|
127
|
-
});
|
|
128
|
-
|
|
129
55
|
it(`emits 'onValueChange' with the new value when a new option has been selected`, () => {
|
|
130
56
|
const onValueChangeSpy = jest.fn();
|
|
131
57
|
const mockValueUpdated = 'updated-test-value';
|
|
132
|
-
const wrapper =
|
|
133
|
-
<RendererWrapper onValueChange={onValueChangeSpy} />,
|
|
134
|
-
);
|
|
58
|
+
const wrapper = mount(<RendererWrapper onValueChange={onValueChangeSpy} />);
|
|
135
59
|
|
|
136
|
-
const select = wrapper.find(
|
|
60
|
+
const select = wrapper.find(Select);
|
|
137
61
|
expect(select.prop('value')).toBe('');
|
|
138
62
|
|
|
139
63
|
act(() => {
|
|
140
|
-
select.
|
|
64
|
+
select.prop('onChange')?.({
|
|
65
|
+
// @ts-expect-error not full event args object
|
|
66
|
+
currentTarget: { value: mockValueUpdated },
|
|
67
|
+
});
|
|
68
|
+
wrapper.update();
|
|
141
69
|
});
|
|
142
70
|
|
|
143
71
|
expect(onValueChangeSpy).toHaveBeenCalledTimes(1);
|
|
@@ -149,17 +77,21 @@ describe('createSelectRenderer', () => {
|
|
|
149
77
|
const mockStringifiedNumber = '5';
|
|
150
78
|
const stringToNumberTransformer = (value: string): number => Number(value);
|
|
151
79
|
|
|
152
|
-
const wrapper =
|
|
80
|
+
const wrapper = mount(
|
|
153
81
|
<RendererWrapper
|
|
154
82
|
onValueChange={onValueChangeSpy}
|
|
155
83
|
transform={stringToNumberTransformer}
|
|
156
84
|
/>,
|
|
157
85
|
);
|
|
158
86
|
|
|
159
|
-
const select = wrapper.find(
|
|
87
|
+
const select = wrapper.find(Select);
|
|
160
88
|
|
|
161
89
|
act(() => {
|
|
162
|
-
select.
|
|
90
|
+
select.prop('onChange')?.({
|
|
91
|
+
// @ts-expect-error not full event args object
|
|
92
|
+
currentTarget: { value: mockStringifiedNumber },
|
|
93
|
+
});
|
|
94
|
+
wrapper.update();
|
|
163
95
|
});
|
|
164
96
|
|
|
165
97
|
expect(onValueChangeSpy).toHaveBeenCalledTimes(1);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import
|
|
2
|
-
import React, { ChangeEvent } from 'react';
|
|
1
|
+
import React, { FormEvent } from 'react';
|
|
3
2
|
import { DynamicListDataEntryRenderer } from '../../../../DynamicDataList/DynamicDataList.model';
|
|
3
|
+
import { Select } from '../../../../FormElements';
|
|
4
4
|
import { CreateSelectRendererConfig } from '../renderers.model';
|
|
5
5
|
import classes from './createSelectRenderer.scss';
|
|
6
6
|
|
|
@@ -17,7 +17,7 @@ import classes from './createSelectRenderer.scss';
|
|
|
17
17
|
*/
|
|
18
18
|
export const createSelectRenderer = ({
|
|
19
19
|
id,
|
|
20
|
-
name,
|
|
20
|
+
name = '',
|
|
21
21
|
placeholder,
|
|
22
22
|
options = [],
|
|
23
23
|
defaultValue,
|
|
@@ -33,34 +33,23 @@ export const createSelectRenderer = ({
|
|
|
33
33
|
onValueChange(defaultValue);
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
const onChangeHandler = (e:
|
|
37
|
-
onValueChange(transform(e.
|
|
36
|
+
const onChangeHandler = (e: FormEvent<HTMLSelectElement>): void => {
|
|
37
|
+
onValueChange(transform(e.currentTarget.value)); // emit onChange with transformed value
|
|
38
38
|
};
|
|
39
39
|
|
|
40
40
|
return (
|
|
41
|
-
<
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
{disabled ? '' : placeholder}
|
|
54
|
-
</option>
|
|
55
|
-
)}
|
|
56
|
-
{options.map((option) => (
|
|
57
|
-
<option key={option.value} value={option.value}>
|
|
58
|
-
{option.label}
|
|
59
|
-
</option>
|
|
60
|
-
))}
|
|
61
|
-
</select>
|
|
62
|
-
{error !== undefined && <small>{error}</small>}
|
|
63
|
-
</div>
|
|
41
|
+
<Select
|
|
42
|
+
id={id}
|
|
43
|
+
name={name}
|
|
44
|
+
disabled={disabled}
|
|
45
|
+
placeholder={placeholder}
|
|
46
|
+
onChange={onChangeHandler}
|
|
47
|
+
inlineMode={true}
|
|
48
|
+
error={error}
|
|
49
|
+
className={classes.container}
|
|
50
|
+
value={(currentValue as string) ?? ''}
|
|
51
|
+
options={options}
|
|
52
|
+
/>
|
|
64
53
|
);
|
|
65
54
|
};
|
|
66
55
|
|
|
@@ -13,6 +13,8 @@ export interface SelectProps extends BaseFormControl, BaseSelectEvents {
|
|
|
13
13
|
autoFocus?: boolean;
|
|
14
14
|
/** Defines whether an empty option should be added as the first option (default: false) */
|
|
15
15
|
addEmptyOption?: boolean;
|
|
16
|
+
/** Select placeholder */
|
|
17
|
+
placeholder?: string;
|
|
16
18
|
}
|
|
17
19
|
|
|
18
20
|
export const Select: React.FC<SelectProps> = ({
|
|
@@ -24,6 +26,7 @@ export const Select: React.FC<SelectProps> = ({
|
|
|
24
26
|
error,
|
|
25
27
|
autoFocus = false,
|
|
26
28
|
addEmptyOption = false,
|
|
29
|
+
placeholder,
|
|
27
30
|
onChange,
|
|
28
31
|
onBlur,
|
|
29
32
|
onFocus,
|
|
@@ -50,7 +53,15 @@ export const Select: React.FC<SelectProps> = ({
|
|
|
50
53
|
onBlur={onBlur}
|
|
51
54
|
onFocus={onFocus}
|
|
52
55
|
>
|
|
53
|
-
{addEmptyOption &&
|
|
56
|
+
{addEmptyOption &&
|
|
57
|
+
(placeholder === null || placeholder === undefined) && (
|
|
58
|
+
<option value=""></option>
|
|
59
|
+
)}
|
|
60
|
+
{placeholder && (
|
|
61
|
+
<option value="" disabled>
|
|
62
|
+
{disabled ? '' : placeholder}
|
|
63
|
+
</option>
|
|
64
|
+
)}
|
|
54
65
|
{options.map((option) => (
|
|
55
66
|
<option key={option.value} value={option.value}>
|
|
56
67
|
{option.label}
|