@axinom/mosaic-ui 0.34.0-rc.3 → 0.34.0-rc.31
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/Explorer/Explorer.d.ts.map +1 -1
- package/dist/components/Filters/Filter/Filter.d.ts +2 -1
- package/dist/components/Filters/Filter/Filter.d.ts.map +1 -1
- package/dist/components/Filters/Filters.d.ts.map +1 -1
- package/dist/components/Filters/Filters.model.d.ts +2 -0
- package/dist/components/Filters/Filters.model.d.ts.map +1 -1
- package/dist/components/FormElements/Checkbox/Checkbox.d.ts.map +1 -1
- package/dist/components/FormElements/Checkbox/CheckboxField.d.ts +1 -1
- package/dist/components/FormElements/Checkbox/CheckboxField.d.ts.map +1 -1
- package/dist/components/FormElements/CustomTags/CustomTagsField.d.ts.map +1 -1
- package/dist/components/FormElements/DateTimeField/DateTimeTextField.d.ts +1 -1
- package/dist/components/FormElements/DateTimeField/DateTimeTextField.d.ts.map +1 -1
- package/dist/components/FormElements/DynamicDataListControl/DynamicDataListField.d.ts.map +1 -1
- package/dist/components/FormElements/FileUploadControl/FileUploadControl.d.ts.map +1 -1
- package/dist/components/FormElements/FileUploadControl/FileUploadField.d.ts.map +1 -1
- package/dist/components/FormElements/MaskedSingleLineText/MaskedSingleLineTextField.d.ts +1 -1
- package/dist/components/FormElements/MaskedSingleLineText/MaskedSingleLineTextField.d.ts.map +1 -1
- package/dist/components/FormElements/SingleLineText/SingleLineText.d.ts.map +1 -1
- package/dist/components/List/List.d.ts.map +1 -1
- package/dist/components/List/List.model.d.ts +2 -0
- package/dist/components/List/List.model.d.ts.map +1 -1
- package/dist/components/List/ListHeader/ListHeader.d.ts +7 -1
- package/dist/components/List/ListHeader/ListHeader.d.ts.map +1 -1
- package/dist/components/List/ListHeader/useResize.d.ts +18 -0
- package/dist/components/List/ListHeader/useResize.d.ts.map +1 -0
- package/dist/components/List/ListRow/ListRow.d.ts.map +1 -1
- package/dist/components/List/ListRow/ListRowLoader.d.ts +2 -2
- package/dist/components/List/ListRow/ListRowLoader.d.ts.map +1 -1
- package/dist/components/List/ListRow/Renderers/BooleanDotRenderer/BooleanDotRenderer.d.ts.map +1 -1
- package/dist/components/List/useColumnsSize.d.ts +21 -0
- package/dist/components/List/useColumnsSize.d.ts.map +1 -0
- package/dist/index.es.js +3 -3
- package/dist/index.es.js.map +1 -1
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/initialize.d.ts +3 -3
- package/dist/initialize.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/components/DynamicDataList/DynamicListDataEntry/Renderers/createInputRenderer/createInputRenderer.spec.tsx +2 -2
- package/src/components/Explorer/Explorer.stories.tsx +16 -0
- package/src/components/Explorer/Explorer.tsx +33 -31
- package/src/components/Filters/Filter/Filter.spec.tsx +24 -1
- package/src/components/Filters/Filter/Filter.tsx +6 -0
- package/src/components/Filters/Filters.model.ts +3 -0
- package/src/components/Filters/Filters.stories.tsx +9 -0
- package/src/components/Filters/Filters.tsx +1 -0
- package/src/components/FormElements/BooleanView/BooleanViewField.scss +4 -6
- package/src/components/FormElements/BooleanView/BooleanViewField.spec.tsx +6 -6
- package/src/components/FormElements/BooleanView/BooleanViewField.tsx +1 -1
- package/src/components/FormElements/Checkbox/Checkbox.tsx +1 -1
- package/src/components/FormElements/Checkbox/CheckboxField.tsx +4 -5
- package/src/components/FormElements/CustomTags/CustomTags.scss +15 -4
- package/src/components/FormElements/CustomTags/CustomTags.spec.tsx +3 -3
- package/src/components/FormElements/CustomTags/CustomTags.tsx +3 -3
- package/src/components/FormElements/CustomTags/CustomTagsField.tsx +1 -2
- package/src/components/FormElements/DateTimeField/DateTimeTextField.tsx +3 -3
- package/src/components/FormElements/DynamicDataListControl/DynamicDataListField.tsx +1 -2
- package/src/components/FormElements/FileUploadControl/FileUploadControl.spec.tsx +35 -0
- package/src/components/FormElements/FileUploadControl/FileUploadControl.tsx +2 -0
- package/src/components/FormElements/FileUploadControl/FileUploadField.tsx +1 -2
- package/src/components/FormElements/FormElementContainer/FormElementContainer.scss +2 -0
- package/src/components/FormElements/MaskedSingleLineText/MaskedSingleLineTextField.tsx +1 -1
- package/src/components/FormElements/Radio/RadioField.tsx +2 -2
- package/src/components/FormElements/SingleLineText/SingleLineText.spec.tsx +5 -4
- package/src/components/FormElements/SingleLineText/SingleLineText.tsx +6 -1
- package/src/components/FormElements/Tags/Tags.scss +1 -1
- package/src/components/FormElements/ToggleButton/ToggleButton.scss +18 -7
- package/src/components/FormStation/FormStation.spec.tsx +12 -6
- package/src/components/FormStation/FormStation.tsx +6 -6
- package/src/components/InlineMenu/InlineMenu.scss +20 -5
- package/src/components/LandingPageTiles/TileLarge/TileLarge.scss +11 -6
- package/src/components/List/List.model.ts +3 -0
- package/src/components/List/List.scss +0 -2
- package/src/components/List/List.stories.tsx +1 -1
- package/src/components/List/List.tsx +17 -55
- package/src/components/List/ListHeader/ListHeader.scss +23 -10
- package/src/components/List/ListHeader/ListHeader.spec.tsx +56 -0
- package/src/components/List/ListHeader/ListHeader.tsx +43 -9
- package/src/components/List/ListHeader/useResize.ts +108 -0
- package/src/components/List/ListRow/ListRow.scss +8 -12
- package/src/components/List/ListRow/ListRow.spec.tsx +5 -21
- package/src/components/List/ListRow/ListRow.tsx +16 -32
- package/src/components/List/ListRow/ListRowLoader.tsx +14 -4
- package/src/components/List/ListRow/Renderers/BooleanDotRenderer/BooleanDotRenderer.scss +10 -8
- package/src/components/List/ListRow/Renderers/BooleanDotRenderer/BooleanDotRenderer.tsx +3 -1
- package/src/components/List/useColumnsSize.ts +120 -0
- package/src/initialize.ts +4 -4
- package/src/styles/variables.scss +11 -0
package/dist/initialize.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AddIndicator, CustomEventEmitter, RemoveIndicator, ShowNotification } from './types/ui-config';
|
|
2
|
-
export declare enum
|
|
2
|
+
export declare enum SaveIndicatorType {
|
|
3
3
|
Saving = "saving",
|
|
4
4
|
Inactive = "inactive",
|
|
5
5
|
Dirty = "dirty"
|
|
@@ -8,7 +8,7 @@ export declare let showNotification: ShowNotification | (() => void);
|
|
|
8
8
|
export declare let addIndicator: AddIndicator | (() => void);
|
|
9
9
|
export declare let removeIndicator: RemoveIndicator | (() => void);
|
|
10
10
|
export declare let on: CustomEventEmitter['on'] | (() => void);
|
|
11
|
-
export declare let setSaveIndicator: (type:
|
|
11
|
+
export declare let setSaveIndicator: (type: SaveIndicatorType) => void;
|
|
12
12
|
/**
|
|
13
13
|
* Passes the PiralApi methods to the UI library.
|
|
14
14
|
* @param app {UiConfig} object containing PiralApi methods for use in UI library.
|
|
@@ -19,6 +19,6 @@ export interface UiConfig {
|
|
|
19
19
|
addIndicator: AddIndicator;
|
|
20
20
|
removeIndicator: RemoveIndicator;
|
|
21
21
|
on: CustomEventEmitter['on'];
|
|
22
|
-
setSaveIndicator: (type:
|
|
22
|
+
setSaveIndicator: (type: SaveIndicatorType) => void;
|
|
23
23
|
}
|
|
24
24
|
//# sourceMappingURL=initialize.d.ts.map
|
package/dist/initialize.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"initialize.d.ts","sourceRoot":"","sources":["../src/initialize.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,kBAAkB,EAClB,eAAe,EACf,gBAAgB,EACjB,MAAM,mBAAmB,CAAC;AAE3B,oBAAY,
|
|
1
|
+
{"version":3,"file":"initialize.d.ts","sourceRoot":"","sources":["../src/initialize.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,kBAAkB,EAClB,eAAe,EACf,gBAAgB,EACjB,MAAM,mBAAmB,CAAC;AAE3B,oBAAY,iBAAiB;IAC3B,MAAM,WAAW;IACjB,QAAQ,aAAa;IACrB,KAAK,UAAU;CAChB;AAED,eAAO,IAAI,gBAAgB,EAAE,gBAAgB,GAAG,CAAC,MAAM,IAAI,CAC7B,CAAC;AAE/B,eAAO,IAAI,YAAY,EAAE,YAAY,GAAG,CAAC,MAAM,IAAI,CAA4B,CAAC;AAEhF,eAAO,IAAI,eAAe,EAAE,eAAe,GAAG,CAAC,MAAM,IAAI,CAC5B,CAAC;AAE9B,eAAO,IAAI,EAAE,EAAE,kBAAkB,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,IAAI,CAAkB,CAAC;AAExE,eAAO,IAAI,gBAAgB,EAAE,CAAC,IAAI,EAAE,iBAAiB,KAAK,IAC5B,CAAC;AAE/B;;;GAGG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,QAAQ,GAAG,IAAI,CAQhD;AAED,MAAM,WAAW,QAAQ;IACvB,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,YAAY,EAAE,YAAY,CAAC;IAC3B,eAAe,EAAE,eAAe,CAAC;IACjC,EAAE,EAAE,kBAAkB,CAAC,IAAI,CAAC,CAAC;IAC7B,gBAAgB,EAAE,CAAC,IAAI,EAAE,iBAAiB,KAAK,IAAI,CAAC;CACrD"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@axinom/mosaic-ui",
|
|
3
|
-
"version": "0.34.0-rc.
|
|
3
|
+
"version": "0.34.0-rc.31",
|
|
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.7-rc.
|
|
35
|
+
"@axinom/mosaic-core": "^0.4.7-rc.31",
|
|
36
36
|
"@faker-js/faker": "^7.4.0",
|
|
37
37
|
"@popperjs/core": "^2.9.2",
|
|
38
38
|
"clsx": "^1.1.0",
|
|
@@ -102,5 +102,5 @@
|
|
|
102
102
|
"publishConfig": {
|
|
103
103
|
"access": "public"
|
|
104
104
|
},
|
|
105
|
-
"gitHead": "
|
|
105
|
+
"gitHead": "76aa11c0c081add5afb09b9885d02546206bd977"
|
|
106
106
|
}
|
|
@@ -49,7 +49,7 @@ describe('createInputRenderer', () => {
|
|
|
49
49
|
|
|
50
50
|
const input = wrapper.find('input');
|
|
51
51
|
|
|
52
|
-
expect(input.
|
|
52
|
+
expect(input.getDOMNode<HTMLInputElement>().value).toBe(mockValue);
|
|
53
53
|
});
|
|
54
54
|
|
|
55
55
|
it(`emits 'onValueChange' with the new value when 'input' value has changed`, () => {
|
|
@@ -59,7 +59,7 @@ describe('createInputRenderer', () => {
|
|
|
59
59
|
|
|
60
60
|
const input = wrapper.find('input');
|
|
61
61
|
|
|
62
|
-
expect(input.
|
|
62
|
+
expect(input.getDOMNode<HTMLInputElement>().value).toBe('');
|
|
63
63
|
|
|
64
64
|
act(() => {
|
|
65
65
|
input.simulate('change', { target: { value: mockValueUpdated } });
|
|
@@ -238,6 +238,22 @@ initializeUi({
|
|
|
238
238
|
action('showNotification')(args);
|
|
239
239
|
return faker.random.numeric();
|
|
240
240
|
},
|
|
241
|
+
addIndicator: (args) => {
|
|
242
|
+
action('addIndicator')(args);
|
|
243
|
+
return Math.random();
|
|
244
|
+
},
|
|
245
|
+
removeIndicator: (args) => {
|
|
246
|
+
action('removeIndicator')(args);
|
|
247
|
+
},
|
|
248
|
+
showSaveIndicator: () => {
|
|
249
|
+
action('showSaveIndicator')();
|
|
250
|
+
},
|
|
251
|
+
hideSaveIndicator: () => {
|
|
252
|
+
action('hideSaveIndicator')();
|
|
253
|
+
},
|
|
254
|
+
on: (event, callback) => {
|
|
255
|
+
action('on')(event, callback);
|
|
256
|
+
},
|
|
241
257
|
});
|
|
242
258
|
|
|
243
259
|
export const ActionErrors: StoryObj<ExplorerStoryType> = {
|
|
@@ -433,37 +433,39 @@ export const Explorer = React.forwardRef(function Explorer<T extends Data>(
|
|
|
433
433
|
setActiveFilters(args);
|
|
434
434
|
}}
|
|
435
435
|
/>
|
|
436
|
-
<
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
onSortChanged(sortData)
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
436
|
+
<div>
|
|
437
|
+
<List<T>
|
|
438
|
+
columns={columns}
|
|
439
|
+
data={data}
|
|
440
|
+
isLoading={isLoading}
|
|
441
|
+
isError={Boolean(stationMessage?.type === 'error')}
|
|
442
|
+
handleRetry={false}
|
|
443
|
+
minimumWidth={minimumWidth}
|
|
444
|
+
columnGap={columnGap}
|
|
445
|
+
rowGap={rowGap}
|
|
446
|
+
headerRowHeight={headerRowHeight}
|
|
447
|
+
listRowHeight={listRowHeight}
|
|
448
|
+
listRowActionSize={listRowActionSize}
|
|
449
|
+
headerRowActionSize={headerRowActionSize}
|
|
450
|
+
horizontalTextAlign={horizontalTextAlign}
|
|
451
|
+
verticalTextAlign={verticalTextAlign}
|
|
452
|
+
keyProperty={keyProperty}
|
|
453
|
+
showActionButton={Boolean(generateItemLink) || Boolean(onItemClicked)} // or hard code to `true`?
|
|
454
|
+
selectionMode={mode}
|
|
455
|
+
enableSelectAll={enableSelectAll}
|
|
456
|
+
loadingTriggerOffset={loadingTriggerOffset}
|
|
457
|
+
defaultSortOrder={sortOrder}
|
|
458
|
+
generateItemLink={generateItemLink}
|
|
459
|
+
onItemClicked={onItemClickedHandler}
|
|
460
|
+
onItemSelected={itemSelectedHandler}
|
|
461
|
+
onRequestMoreData={onRequestMoreData}
|
|
462
|
+
onSortChanged={(sortData: SortData<T>) => {
|
|
463
|
+
onSortChanged(sortData);
|
|
464
|
+
setSortOrder(sortData);
|
|
465
|
+
}}
|
|
466
|
+
inlineMenuActions={inlineMenuActions && inlineMenuActionsHandler}
|
|
467
|
+
/>
|
|
468
|
+
</div>
|
|
467
469
|
</div>
|
|
468
470
|
);
|
|
469
471
|
});
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { mount, shallow } from 'enzyme';
|
|
2
2
|
import React from 'react';
|
|
3
3
|
import { Button } from '../../Buttons';
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
CustomFilterProps,
|
|
6
|
+
FilterType,
|
|
7
|
+
FilterTypes,
|
|
8
|
+
SelectedValueRenderer,
|
|
9
|
+
} from '../Filters.model';
|
|
5
10
|
import { FreeTextFilter } from '../SelectionTypes/FreeTextFilter/FreeTextFilter';
|
|
6
11
|
import { OptionsFilter } from '../SelectionTypes/OptionsFilter/OptionsFilter';
|
|
7
12
|
import { Filter, FilterProps } from './Filter';
|
|
@@ -148,6 +153,24 @@ describe('Filter', () => {
|
|
|
148
153
|
expect(wrapper.find('.selectedValue').exists()).toBe(false);
|
|
149
154
|
});
|
|
150
155
|
|
|
156
|
+
it(`'selectedValueRenderer' value is displayed when the prop is set`, () => {
|
|
157
|
+
const renderedValue = `${sampleText} renderer`;
|
|
158
|
+
const selectedValueRenderer: SelectedValueRenderer = (value) => {
|
|
159
|
+
return `${value} renderer`;
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
const wrapper = mount(
|
|
163
|
+
<Filter {...defaultProps} options={freeTextFilter} value={sampleText} />,
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
expect(wrapper.find('.selectedValue').text()).toEqual(sampleText);
|
|
167
|
+
|
|
168
|
+
wrapper.setProps({ selectedValueRenderer });
|
|
169
|
+
wrapper.update();
|
|
170
|
+
|
|
171
|
+
expect(wrapper.find('.selectedValue').text()).toEqual(renderedValue);
|
|
172
|
+
});
|
|
173
|
+
|
|
151
174
|
it('Raises onFilterChange with prop name and new undefined value when filter is closed', () => {
|
|
152
175
|
const mockValue = 'test-value';
|
|
153
176
|
const wrapper = mount(
|
|
@@ -26,6 +26,7 @@ export interface FilterProps<T extends Data> {
|
|
|
26
26
|
index?: number;
|
|
27
27
|
isActive: boolean;
|
|
28
28
|
className?: string;
|
|
29
|
+
selectedValueRenderer?: (value: FilterValue) => string;
|
|
29
30
|
onFilterChange: (prop: keyof T, value: FilterValue, index: number) => void;
|
|
30
31
|
onValidate?: (currentValue: FilterValue) => string | null | undefined;
|
|
31
32
|
onFilterClicked: () => void;
|
|
@@ -37,6 +38,7 @@ export const Filter = <T extends Data>({
|
|
|
37
38
|
index = -1,
|
|
38
39
|
isActive,
|
|
39
40
|
className = '',
|
|
41
|
+
selectedValueRenderer,
|
|
40
42
|
onFilterChange,
|
|
41
43
|
onFilterClicked,
|
|
42
44
|
onValidate,
|
|
@@ -72,6 +74,10 @@ export const Filter = <T extends Data>({
|
|
|
72
74
|
};
|
|
73
75
|
|
|
74
76
|
const renderValue = (value: unknown): ReactNode => {
|
|
77
|
+
if (selectedValueRenderer !== undefined) {
|
|
78
|
+
return selectedValueRenderer(value);
|
|
79
|
+
}
|
|
80
|
+
|
|
75
81
|
switch (options.type) {
|
|
76
82
|
case FilterTypes.Date:
|
|
77
83
|
return formatDate(String(value));
|
|
@@ -15,6 +15,7 @@ export interface FilterConfig<T extends Data> {
|
|
|
15
15
|
label: string;
|
|
16
16
|
property: keyof T;
|
|
17
17
|
type: FilterTypes;
|
|
18
|
+
selectedValueRenderer?: SelectedValueRenderer;
|
|
18
19
|
onValidate?: FilterValidatorFunction<T>;
|
|
19
20
|
}
|
|
20
21
|
|
|
@@ -105,3 +106,5 @@ export type FilterValidatorFunction<T> = (
|
|
|
105
106
|
value: FilterValue,
|
|
106
107
|
allValues: FilterValues<T>,
|
|
107
108
|
) => FilterValidationResult;
|
|
109
|
+
|
|
110
|
+
export type SelectedValueRenderer = (value: FilterValue) => string;
|
|
@@ -153,6 +153,15 @@ const customFilter: FilterType<{ custom: string }> = {
|
|
|
153
153
|
label: 'Custom Filter (multi select)',
|
|
154
154
|
property: 'custom',
|
|
155
155
|
type: FilterTypes.Custom,
|
|
156
|
+
selectedValueRenderer: (value: unknown) => {
|
|
157
|
+
const items = value as string[];
|
|
158
|
+
|
|
159
|
+
return String(
|
|
160
|
+
`${items.length} item${items.length === 1 ? '' : 's'}: ${items
|
|
161
|
+
.join(', ')
|
|
162
|
+
.replace(/([a-zA-Z])(\d)/g, '$1 $2')}`,
|
|
163
|
+
);
|
|
164
|
+
},
|
|
156
165
|
component: CustomFilterComponent,
|
|
157
166
|
};
|
|
158
167
|
|
|
@@ -134,6 +134,7 @@ export const Filters = <T extends Data>({
|
|
|
134
134
|
index={index}
|
|
135
135
|
isActive={index === activeFilterIndex}
|
|
136
136
|
onFilterChange={filtersChangedHandler}
|
|
137
|
+
selectedValueRenderer={filter.selectedValueRenderer}
|
|
137
138
|
onValidate={(currentValue) =>
|
|
138
139
|
filter.onValidate &&
|
|
139
140
|
filter.onValidate(currentValue, activeFilters.current)
|
|
@@ -8,18 +8,16 @@
|
|
|
8
8
|
font-size: var(--label-font-size, $label-font-size);
|
|
9
9
|
align-items: center;
|
|
10
10
|
|
|
11
|
-
.
|
|
12
|
-
background-
|
|
11
|
+
.false {
|
|
12
|
+
background-image: url("data:image/svg+xml, %3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 40 40' %3E%3Cpath vector-effect='non-scaling-stroke' fill='none' stroke='%23707070' stroke-width='2' d='M20.1,2.4c9.9,0,18,8.1,18,18s-8.1,18-18,18s-18-8.1-18-18 S10.2,2.4,20.1,2.4z M28.6,20H11.4'/%3E%3C/svg%3E");
|
|
13
13
|
height: 20px;
|
|
14
14
|
width: 20px;
|
|
15
|
-
border-radius: 100%;
|
|
16
15
|
}
|
|
17
16
|
|
|
18
|
-
.
|
|
19
|
-
background-
|
|
17
|
+
.true {
|
|
18
|
+
background-image: url("data:image/svg+xml, %3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 40 40' %3E%3Cpath vector-effect='non-scaling-stroke' fill='none' stroke='%2395C842' stroke-width='2' d='M20,2c9.9,0,18,8.1,18,18s-8.1,18-18,18S2,29.9,2,20S10.1,2,20,2z M29,13.5 L17.8,26.3L11,19.1'/%3E%3C/svg%3E");
|
|
20
19
|
height: 20px;
|
|
21
20
|
width: 20px;
|
|
22
|
-
border-radius: 100%;
|
|
23
21
|
}
|
|
24
22
|
}
|
|
25
23
|
}
|
|
@@ -40,8 +40,8 @@ describe('BooleanViewField', () => {
|
|
|
40
40
|
/>,
|
|
41
41
|
);
|
|
42
42
|
|
|
43
|
-
const green = wrapper.find('.
|
|
44
|
-
const red = wrapper.find('.
|
|
43
|
+
const green = wrapper.find('.true');
|
|
44
|
+
const red = wrapper.find('.false');
|
|
45
45
|
const text = wrapper.find('.text');
|
|
46
46
|
|
|
47
47
|
expect(green.exists()).toBe(true);
|
|
@@ -61,8 +61,8 @@ describe('BooleanViewField', () => {
|
|
|
61
61
|
/>,
|
|
62
62
|
);
|
|
63
63
|
|
|
64
|
-
const green = wrapper.find('.
|
|
65
|
-
const red = wrapper.find('.
|
|
64
|
+
const green = wrapper.find('.true');
|
|
65
|
+
const red = wrapper.find('.false');
|
|
66
66
|
const text = wrapper.find('.text');
|
|
67
67
|
|
|
68
68
|
expect(green.exists()).toBe(false);
|
|
@@ -73,8 +73,8 @@ describe('BooleanViewField', () => {
|
|
|
73
73
|
it('defaults to show red circle', () => {
|
|
74
74
|
const wrapper = shallow(<BooleanViewField />);
|
|
75
75
|
|
|
76
|
-
const green = wrapper.find('.
|
|
77
|
-
const red = wrapper.find('.
|
|
76
|
+
const green = wrapper.find('.true');
|
|
77
|
+
const red = wrapper.find('.false');
|
|
78
78
|
|
|
79
79
|
expect(green.exists()).toBe(false);
|
|
80
80
|
expect(red.exists()).toBe(true);
|
|
@@ -31,7 +31,7 @@ export const BooleanViewField: React.FC<BooleanViewFieldProps> = ({
|
|
|
31
31
|
dataTestFieldType="BooleanView"
|
|
32
32
|
>
|
|
33
33
|
<div className={clsx(classes.value)}>
|
|
34
|
-
<div className={clsx(value ? classes.
|
|
34
|
+
<div className={clsx(value ? classes.true : classes.false)}></div>
|
|
35
35
|
<div
|
|
36
36
|
className={clsx(classes.text)}
|
|
37
37
|
data-test-id="form-field-value"
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import clsx from 'clsx';
|
|
2
2
|
import React, { useRef, useState } from 'react';
|
|
3
3
|
import {
|
|
4
|
+
ConfirmDialog,
|
|
4
5
|
ConfirmationConfig,
|
|
5
6
|
ConfirmationMode,
|
|
6
|
-
ConfirmDialog,
|
|
7
7
|
} from '../../ConfirmDialog';
|
|
8
8
|
import { BaseFormControl, BaseInputEvents } from '../Form.models';
|
|
9
9
|
import { FormElementContainer } from '../FormElementContainer';
|
|
@@ -2,12 +2,11 @@ import { useField } from 'formik';
|
|
|
2
2
|
import React from 'react';
|
|
3
3
|
import { useFormikError } from '../useFormikError';
|
|
4
4
|
import { Checkbox, CheckboxProps } from './Checkbox';
|
|
5
|
-
export const CheckboxField: React.FC<
|
|
6
|
-
|
|
7
|
-
) => {
|
|
8
|
-
const { name } = props;
|
|
5
|
+
export const CheckboxField: React.FC<
|
|
6
|
+
Omit<CheckboxProps, 'error' | 'onChange'>
|
|
7
|
+
> = (props) => {
|
|
9
8
|
const error = useFormikError(props.name);
|
|
10
|
-
const [field, , helpers] = useField(name);
|
|
9
|
+
const [field, , helpers] = useField(props.name);
|
|
11
10
|
|
|
12
11
|
return (
|
|
13
12
|
<Checkbox
|
|
@@ -127,7 +127,10 @@
|
|
|
127
127
|
stroke: white;
|
|
128
128
|
}
|
|
129
129
|
|
|
130
|
-
background-color:
|
|
130
|
+
background-color: var(
|
|
131
|
+
--tag-plus-button-bg-color,
|
|
132
|
+
$tag-plus-button-bg-color
|
|
133
|
+
);
|
|
131
134
|
position: absolute;
|
|
132
135
|
right: 1px;
|
|
133
136
|
top: 1px;
|
|
@@ -146,8 +149,16 @@
|
|
|
146
149
|
|
|
147
150
|
&:enabled {
|
|
148
151
|
&:hover {
|
|
149
|
-
border: 1px solid
|
|
150
|
-
|
|
152
|
+
border: 1px solid
|
|
153
|
+
var(
|
|
154
|
+
--tag-plus-button-hover-stroke-color,
|
|
155
|
+
$tag-plus-button-hover-stroke-color
|
|
156
|
+
);
|
|
157
|
+
box-shadow: 0 0 0 2px
|
|
158
|
+
var(
|
|
159
|
+
--tag-plus-button-hover-stroke-color,
|
|
160
|
+
$tag-plus-button-hover-stroke-color
|
|
161
|
+
);
|
|
151
162
|
|
|
152
163
|
&.hasError {
|
|
153
164
|
border: 1px solid
|
|
@@ -164,7 +175,7 @@
|
|
|
164
175
|
|
|
165
176
|
.customTagEnter {
|
|
166
177
|
opacity: 0;
|
|
167
|
-
background-color:
|
|
178
|
+
background-color: var(--tag-enter-color, $tag-enter-color) !important;
|
|
168
179
|
}
|
|
169
180
|
|
|
170
181
|
.customTagEnterActive {
|
|
@@ -70,7 +70,7 @@ describe('CustomTags', () => {
|
|
|
70
70
|
|
|
71
71
|
it('displays a tag for each currently selected tag', () => {
|
|
72
72
|
const mockCurrentTags = ['1', '2', '3'];
|
|
73
|
-
const wrapper =
|
|
73
|
+
const wrapper = mount(
|
|
74
74
|
<CustomTags name={'test-name'} value={mockCurrentTags} />,
|
|
75
75
|
);
|
|
76
76
|
|
|
@@ -361,7 +361,7 @@ describe('CustomTags', () => {
|
|
|
361
361
|
const onChangeSpy = jest.fn();
|
|
362
362
|
const mockCurrentTags = ['1', '2', '3'];
|
|
363
363
|
const mockTag = '';
|
|
364
|
-
const wrapper =
|
|
364
|
+
const wrapper = mount(
|
|
365
365
|
<CustomTags
|
|
366
366
|
name={'test-name'}
|
|
367
367
|
onChange={onChangeSpy}
|
|
@@ -394,7 +394,7 @@ describe('CustomTags', () => {
|
|
|
394
394
|
const onChangeSpy = jest.fn();
|
|
395
395
|
const mockCurrentTags = ['1', '2', '3'];
|
|
396
396
|
const mockTag = '2';
|
|
397
|
-
const wrapper =
|
|
397
|
+
const wrapper = mount(
|
|
398
398
|
<CustomTags
|
|
399
399
|
name={'test-name'}
|
|
400
400
|
onChange={onChangeSpy}
|
|
@@ -56,7 +56,7 @@ export const CustomTags: React.FC<CustomTagsProps> = ({
|
|
|
56
56
|
id,
|
|
57
57
|
name,
|
|
58
58
|
type = 'text',
|
|
59
|
-
value
|
|
59
|
+
value,
|
|
60
60
|
disabled = false,
|
|
61
61
|
placeholder,
|
|
62
62
|
error = undefined,
|
|
@@ -70,12 +70,12 @@ export const CustomTags: React.FC<CustomTagsProps> = ({
|
|
|
70
70
|
className = '',
|
|
71
71
|
...rest
|
|
72
72
|
}) => {
|
|
73
|
-
const [currentTags, setCurrentTags] = useState<string[]>(
|
|
73
|
+
const [currentTags, setCurrentTags] = useState<string[]>([]); // Current tags the user has selected
|
|
74
74
|
const [shouldAnimate, setShouldAnimate] = useState<boolean>(false);
|
|
75
75
|
const ref = useRef<FormEvent<HTMLInputElement>>();
|
|
76
76
|
|
|
77
77
|
useEffect(() => {
|
|
78
|
-
setCurrentTags(
|
|
78
|
+
setCurrentTags(value ?? []);
|
|
79
79
|
}, [value]);
|
|
80
80
|
|
|
81
81
|
const styles = {
|
|
@@ -6,9 +6,8 @@ import { CustomTags, CustomTagsProps } from './CustomTags';
|
|
|
6
6
|
export const CustomTagsField: React.FC<
|
|
7
7
|
Omit<CustomTagsProps, 'error' | 'onChange'>
|
|
8
8
|
> = (props) => {
|
|
9
|
-
const { name } = props;
|
|
10
9
|
const error = useFormikError(props.name);
|
|
11
|
-
const [field, , helpers] = useField(name);
|
|
10
|
+
const [field, , helpers] = useField(props.name);
|
|
12
11
|
|
|
13
12
|
return (
|
|
14
13
|
<CustomTags
|
|
@@ -8,9 +8,9 @@ import { DateTimeText, DateTimeTextProps } from './DateTimeText';
|
|
|
8
8
|
* @example
|
|
9
9
|
* <Field name="title" label="Title" as={DateTimeTextField} />
|
|
10
10
|
*/
|
|
11
|
-
export const DateTimeTextField: React.FC<
|
|
12
|
-
|
|
13
|
-
) => {
|
|
11
|
+
export const DateTimeTextField: React.FC<
|
|
12
|
+
Omit<DateTimeTextProps, 'error' | 'onChange'>
|
|
13
|
+
> = (props) => {
|
|
14
14
|
const error = useFormikError(props.name);
|
|
15
15
|
const [, meta, helpers] = useField(props.name);
|
|
16
16
|
|
|
@@ -24,9 +24,8 @@ export const DynamicDataListField = <T extends Data>(
|
|
|
24
24
|
Omit<DynamicDataListControlProps<T>, 'error' | 'onChange'>
|
|
25
25
|
>,
|
|
26
26
|
): JSX.Element => {
|
|
27
|
-
const { name } = props;
|
|
28
27
|
const error = useFormikError(props.name);
|
|
29
|
-
const [field, , helpers] = useField(name);
|
|
28
|
+
const [field, , helpers] = useField(props.name);
|
|
30
29
|
|
|
31
30
|
return (
|
|
32
31
|
<DynamicDataListControl
|
|
@@ -203,6 +203,41 @@ describe('FileUploadControl', () => {
|
|
|
203
203
|
);
|
|
204
204
|
});
|
|
205
205
|
|
|
206
|
+
it('allows files with the same name to be uploaded multiple times', () => {
|
|
207
|
+
const spy = jest.fn();
|
|
208
|
+
|
|
209
|
+
const wrapper = mount(
|
|
210
|
+
<FileUploadControl name={'test-name'} onFileSelected={spy} />,
|
|
211
|
+
);
|
|
212
|
+
|
|
213
|
+
const fileInput = wrapper.find('input[type="file"]');
|
|
214
|
+
|
|
215
|
+
// Simulate first file upload
|
|
216
|
+
fileInput.simulate('change', { target: { files: mockFileList } });
|
|
217
|
+
|
|
218
|
+
expect(spy).toHaveBeenCalledTimes(1);
|
|
219
|
+
expect(spy).toHaveBeenCalledWith({
|
|
220
|
+
file: mockFile,
|
|
221
|
+
uploadCompleted: expect.any(Function),
|
|
222
|
+
uploadProgress: expect.any(Function),
|
|
223
|
+
uploadStarted: expect.any(Function),
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
// Reset spy
|
|
227
|
+
spy.mockReset();
|
|
228
|
+
|
|
229
|
+
// Simulate second file upload with the same file
|
|
230
|
+
fileInput.simulate('change', { target: { files: mockFileList } });
|
|
231
|
+
|
|
232
|
+
expect(spy).toHaveBeenCalledTimes(1);
|
|
233
|
+
expect(spy).toHaveBeenCalledWith({
|
|
234
|
+
file: mockFile,
|
|
235
|
+
uploadCompleted: expect.any(Function),
|
|
236
|
+
uploadProgress: expect.any(Function),
|
|
237
|
+
uploadStarted: expect.any(Function),
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
|
|
206
241
|
describe('MIME Types', () => {
|
|
207
242
|
const mockTypes = 'image/jpeg,image/png';
|
|
208
243
|
const mockFile = new File([new ArrayBuffer(1)], 'image.png', {
|
|
@@ -169,6 +169,8 @@ export const FileUploadControl: React.FC<FileUploadProps> = ({
|
|
|
169
169
|
onChange={(event) => {
|
|
170
170
|
const file = event.target.files?.item(0);
|
|
171
171
|
file && fileSelected(file);
|
|
172
|
+
// Reset the value of the input field to ensure onChange fires even with same file
|
|
173
|
+
event.target.value = '';
|
|
172
174
|
}}
|
|
173
175
|
/>
|
|
174
176
|
</>
|
|
@@ -6,9 +6,8 @@ import { FileUploadControl, FileUploadProps } from './FileUploadControl';
|
|
|
6
6
|
export const FileUploadField: React.FC<
|
|
7
7
|
Omit<FileUploadProps, 'onFileSelected' | 'error'>
|
|
8
8
|
> = (props) => {
|
|
9
|
-
const { name } = props;
|
|
10
9
|
const error = useFormikError(props.name);
|
|
11
|
-
const [field, , helpers] = useField(name);
|
|
10
|
+
const [field, , helpers] = useField(props.name);
|
|
12
11
|
|
|
13
12
|
return (
|
|
14
13
|
<FileUploadControl
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
placeholderChar={'0'}, />
|
|
18
18
|
*/
|
|
19
19
|
export const MaskedSingleLineTextField: React.FC<
|
|
20
|
-
Omit<MaskedSingleLineTextProps, 'error'>
|
|
20
|
+
Omit<MaskedSingleLineTextProps, 'error' | 'onChange'>
|
|
21
21
|
> = (props) => {
|
|
22
22
|
const error = useFormikError(props.name);
|
|
23
23
|
|
|
@@ -9,9 +9,9 @@ export const RadioField: React.FC<
|
|
|
9
9
|
transform?: (value: unknown) => unknown;
|
|
10
10
|
}
|
|
11
11
|
> = (props) => {
|
|
12
|
-
const {
|
|
12
|
+
const { transform = (value: unknown) => value } = props;
|
|
13
13
|
const error = useFormikError(props.name);
|
|
14
|
-
const [field, , helpers] = useField(name);
|
|
14
|
+
const [field, , helpers] = useField(props.name);
|
|
15
15
|
|
|
16
16
|
return (
|
|
17
17
|
<Radio
|
|
@@ -24,18 +24,20 @@ describe('SingleLineText', () => {
|
|
|
24
24
|
const spy = jest.fn();
|
|
25
25
|
const mockValue = 'test-value';
|
|
26
26
|
const mockValueUpdated = 'updated-test-value';
|
|
27
|
-
const wrapper =
|
|
27
|
+
const wrapper = mount(
|
|
28
28
|
<SingleLineText name={'test-name'} value={mockValue} onChange={spy} />,
|
|
29
29
|
);
|
|
30
30
|
|
|
31
31
|
const input = wrapper.find('input');
|
|
32
32
|
|
|
33
|
-
expect(input.
|
|
33
|
+
expect(input.getDOMNode<HTMLInputElement>().value).toEqual(mockValue);
|
|
34
34
|
|
|
35
35
|
input.simulate('change', { target: { value: mockValueUpdated } });
|
|
36
36
|
|
|
37
37
|
expect(spy).toHaveBeenCalledTimes(1);
|
|
38
|
-
expect(spy).toHaveBeenCalledWith(
|
|
38
|
+
expect(spy).toHaveBeenCalledWith(
|
|
39
|
+
expect.objectContaining({ target: { value: mockValueUpdated } }),
|
|
40
|
+
);
|
|
39
41
|
});
|
|
40
42
|
|
|
41
43
|
it('uses optional props when passed in', () => {
|
|
@@ -48,7 +50,6 @@ describe('SingleLineText', () => {
|
|
|
48
50
|
name: 'test-name',
|
|
49
51
|
placeholder: 'test-placeholder',
|
|
50
52
|
type: 'number',
|
|
51
|
-
value: '',
|
|
52
53
|
} as Record<string, unknown>;
|
|
53
54
|
|
|
54
55
|
const wrapper = shallow(
|