@axinom/mosaic-ui 0.66.0-rc.2 → 0.66.0-rc.21
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/DynamicListHeader/DynamicListHeader.d.ts.map +1 -1
- package/dist/components/Explorer/BulkEdit/FormFieldsConfigConverter.d.ts.map +1 -1
- package/dist/components/FieldSelection/FieldSelection.d.ts.map +1 -1
- package/dist/components/Filters/Filter/Filter.d.ts.map +1 -1
- package/dist/components/Filters/Filters.model.d.ts +5 -0
- package/dist/components/Filters/Filters.model.d.ts.map +1 -1
- package/dist/components/Filters/SelectionTypes/DateTimeFilter/DateTimeFilter.d.ts +2 -0
- package/dist/components/Filters/SelectionTypes/DateTimeFilter/DateTimeFilter.d.ts.map +1 -1
- package/dist/components/Filters/SelectionTypes/FreeTextFilter/FreeTextFilter.d.ts +2 -0
- package/dist/components/Filters/SelectionTypes/FreeTextFilter/FreeTextFilter.d.ts.map +1 -1
- package/dist/components/Filters/SelectionTypes/MultiOptionFilter/MultiOptionFilter.d.ts +2 -0
- package/dist/components/Filters/SelectionTypes/MultiOptionFilter/MultiOptionFilter.d.ts.map +1 -1
- package/dist/components/Filters/SelectionTypes/NumericTextFilter/NumericTextFilter.d.ts +2 -0
- package/dist/components/Filters/SelectionTypes/NumericTextFilter/NumericTextFilter.d.ts.map +1 -1
- package/dist/components/Filters/SelectionTypes/OptionsFilter/OptionsFilter.d.ts +2 -0
- package/dist/components/Filters/SelectionTypes/OptionsFilter/OptionsFilter.d.ts.map +1 -1
- package/dist/components/Filters/SelectionTypes/SearcheableOptionsFilter/SearcheableOptionsFilter.d.ts +2 -0
- package/dist/components/Filters/SelectionTypes/SearcheableOptionsFilter/SearcheableOptionsFilter.d.ts.map +1 -1
- package/dist/components/FormElements/Radio/Radio.d.ts.map +1 -1
- package/dist/components/FormElements/ToggleButton/ToggleButton.d.ts.map +1 -1
- package/dist/components/Hub/Tile/Tile.d.ts.map +1 -1
- package/dist/components/Icons/Icons.d.ts +4 -9
- package/dist/components/Icons/Icons.d.ts.map +1 -1
- package/dist/components/LandingPageTiles/TileLarge/TileLarge.d.ts.map +1 -1
- package/dist/components/LandingPageTiles/TileSmall/TileSmall.d.ts.map +1 -1
- package/dist/components/List/ListCheckBox/ListCheckBox.d.ts.map +1 -1
- package/dist/components/List/ListHeader/ColumnLabel/ColumnLabel.d.ts.map +1 -1
- package/dist/components/List/ListHeader/ListHeader.d.ts.map +1 -1
- package/dist/components/List/ListRow/ListRow.d.ts.map +1 -1
- package/dist/components/List/ListRow/ListRowCell/ListRowCell.d.ts +15 -0
- package/dist/components/List/ListRow/ListRowCell/ListRowCell.d.ts.map +1 -0
- package/dist/components/List/ListRow/ListRowCell/renderData.d.ts +9 -0
- package/dist/components/List/ListRow/ListRowCell/renderData.d.ts.map +1 -0
- package/dist/components/List/ListRow/Renderers/TagsRenderer/TagsRenderer.d.ts.map +1 -1
- package/dist/components/Loaders/ImageLoader/ImageLoader.d.ts.map +1 -1
- package/dist/components/PageHeader/PageHeaderAction/PageHeaderAction.d.ts.map +1 -1
- package/dist/components/VisualElements/ImgElement.d.ts +50 -0
- package/dist/components/VisualElements/ImgElement.d.ts.map +1 -0
- package/dist/components/VisualElements/SvgElement.d.ts +14 -0
- package/dist/components/VisualElements/SvgElement.d.ts.map +1 -0
- package/dist/components/VisualElements/index.d.ts +3 -0
- package/dist/components/VisualElements/index.d.ts.map +1 -0
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/helpers/idleCallbackHelpers.d.ts +42 -0
- package/dist/helpers/idleCallbackHelpers.d.ts.map +1 -0
- package/dist/helpers/index.d.ts +1 -0
- package/dist/helpers/index.d.ts.map +1 -1
- package/dist/hooks/useResize/ResizeIndicator.d.ts +8 -0
- package/dist/hooks/useResize/ResizeIndicator.d.ts.map +1 -0
- package/dist/hooks/useResize/useResize.d.ts +5 -2
- package/dist/hooks/useResize/useResize.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 +2 -2
- package/src/components/DynamicDataList/DynamicListHeader/DynamicListHeader.spec.tsx +2 -0
- package/src/components/DynamicDataList/DynamicListHeader/DynamicListHeader.tsx +62 -50
- package/src/components/Explorer/BulkEdit/FormFieldsConfigConverter.tsx +5 -21
- package/src/components/Explorer/Explorer.stories.tsx +17 -0
- package/src/components/FieldSelection/FieldSelection.scss +4 -0
- package/src/components/FieldSelection/FieldSelection.tsx +1 -0
- package/src/components/Filters/Filter/Filter.scss +34 -15
- package/src/components/Filters/Filter/Filter.spec.tsx +1 -1
- package/src/components/Filters/Filter/Filter.tsx +46 -34
- package/src/components/Filters/Filters.model.ts +6 -0
- package/src/components/Filters/SelectionTypes/DateTimeFilter/DateTimeFilter.tsx +6 -1
- package/src/components/Filters/SelectionTypes/FreeTextFilter/FreeTextFilter.tsx +4 -0
- package/src/components/Filters/SelectionTypes/MultiOptionFilter/MultiOptionFilter.tsx +9 -1
- package/src/components/Filters/SelectionTypes/NumericTextFilter/NumericTextFilter.tsx +5 -0
- package/src/components/Filters/SelectionTypes/OptionsFilter/OptionsFilter.scss +6 -10
- package/src/components/Filters/SelectionTypes/OptionsFilter/OptionsFilter.tsx +8 -0
- package/src/components/Filters/SelectionTypes/SearcheableOptionsFilter/SearcheableOptionsFilter.tsx +6 -1
- package/src/components/FormElements/Radio/Radio.tsx +3 -2
- package/src/components/FormElements/Select/Select.scss +11 -6
- package/src/components/FormElements/ToggleButton/ToggleButton.tsx +32 -27
- package/src/components/Hub/Hub.stories.tsx +3 -2
- package/src/components/Hub/Tile/Tile.spec.tsx +7 -2
- package/src/components/Hub/Tile/Tile.tsx +2 -1
- package/src/components/Icons/Icons.scss +1 -0
- package/src/components/Icons/Icons.spec.tsx +90 -41
- package/src/components/Icons/Icons.tsx +357 -765
- package/src/components/InfoTooltip/InfoTooltip.scss +1 -1
- package/src/components/InlineMenu/InlineMenu.scss +1 -1
- package/src/components/LandingPageTiles/LandingPageTiles.stories.tsx +3 -2
- package/src/components/LandingPageTiles/TileLarge/TileLarge.spec.tsx +5 -1
- package/src/components/LandingPageTiles/TileLarge/TileLarge.tsx +2 -1
- package/src/components/LandingPageTiles/TileSmall/TileSmall.spec.tsx +7 -2
- package/src/components/LandingPageTiles/TileSmall/TileSmall.tsx +2 -1
- package/src/components/List/ListCheckBox/ListCheckBox.tsx +1 -0
- package/src/components/List/ListHeader/ColumnLabel/ColumnLabel.spec.tsx +6 -6
- package/src/components/List/ListHeader/ColumnLabel/ColumnLabel.tsx +10 -13
- package/src/components/List/ListHeader/ListHeader.scss +0 -1
- package/src/components/List/ListHeader/ListHeader.spec.tsx +2 -0
- package/src/components/List/ListHeader/ListHeader.tsx +57 -51
- package/src/components/List/ListRow/ListRow.scss +0 -27
- package/src/components/List/ListRow/ListRow.spec.tsx +10 -8
- package/src/components/List/ListRow/ListRow.tsx +20 -152
- package/src/components/List/ListRow/ListRowCell/ListRowCell.scss +26 -0
- package/src/components/List/ListRow/ListRowCell/ListRowCell.spec.tsx +491 -0
- package/src/components/List/ListRow/ListRowCell/ListRowCell.tsx +57 -0
- package/src/components/List/ListRow/ListRowCell/renderData.tsx +124 -0
- package/src/components/List/ListRow/Renderers/TagsRenderer/TagsRenderer.scss +2 -1
- package/src/components/List/ListRow/Renderers/TagsRenderer/TagsRenderer.spec.tsx +187 -104
- package/src/components/List/ListRow/Renderers/TagsRenderer/TagsRenderer.tsx +134 -80
- package/src/components/Loaders/ImageLoader/ImageLoader.spec.tsx +13 -14
- package/src/components/Loaders/ImageLoader/ImageLoader.tsx +5 -3
- package/src/components/PageHeader/PageHeaderAction/PageHeaderAction.tsx +13 -2
- package/src/components/Utils/Postgraphile/CreateConnectionRenderer.spec.ts +22 -75
- package/src/components/VisualElements/ImgElement.spec.tsx +92 -0
- package/src/components/VisualElements/ImgElement.tsx +72 -0
- package/src/components/VisualElements/SvgElement.spec.tsx +160 -0
- package/src/components/VisualElements/SvgElement.tsx +40 -0
- package/src/components/VisualElements/index.ts +7 -0
- package/src/components/index.ts +1 -0
- package/src/helpers/idleCallbackHelpers.ts +66 -0
- package/src/helpers/index.ts +5 -0
- package/src/hooks/useResize/ResizeIndicator.scss +7 -0
- package/src/hooks/useResize/ResizeIndicator.tsx +39 -0
- package/src/hooks/useResize/{useResize.ts → useResize.tsx} +38 -6
- package/src/styles/variables.scss +7 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@axinom/mosaic-ui",
|
|
3
|
-
"version": "0.66.0-rc.
|
|
3
|
+
"version": "0.66.0-rc.21",
|
|
4
4
|
"description": "UI components for building Axinom Mosaic applications",
|
|
5
5
|
"author": "Axinom",
|
|
6
6
|
"license": "PROPRIETARY",
|
|
@@ -112,5 +112,5 @@
|
|
|
112
112
|
"publishConfig": {
|
|
113
113
|
"access": "public"
|
|
114
114
|
},
|
|
115
|
-
"gitHead": "
|
|
115
|
+
"gitHead": "f35519f8955016e3bc4bdab24de6504822122a08"
|
|
116
116
|
}
|
|
@@ -70,60 +70,72 @@ export const DynamicListHeader = <T extends Data>({
|
|
|
70
70
|
customStyles.top = 0;
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
const { cols, mouseDown } = useResize(
|
|
73
|
+
const { cols, mouseDown, ResizeIndicator } = useResize(
|
|
74
|
+
columns,
|
|
75
|
+
onColumnSizesChanged,
|
|
76
|
+
);
|
|
74
77
|
|
|
75
78
|
return (
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
data-test-id="dynamic-list-header-cell"
|
|
89
|
-
>
|
|
90
|
-
<p className={clsx(classes.position)}>
|
|
91
|
-
{positionLabel ?? (allowDragging ? 'Position' : 'Pos')}
|
|
92
|
-
</p>
|
|
79
|
+
<>
|
|
80
|
+
{ResizeIndicator}
|
|
81
|
+
<div
|
|
82
|
+
className={clsx(
|
|
83
|
+
classes.container,
|
|
84
|
+
'dynamic-list-header-container',
|
|
85
|
+
className,
|
|
86
|
+
)}
|
|
87
|
+
style={customStyles}
|
|
88
|
+
data-test-id="dynamic-list-header"
|
|
89
|
+
>
|
|
90
|
+
{showPositionColumn && (
|
|
93
91
|
<div
|
|
94
|
-
className={clsx(classes.
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
92
|
+
className={clsx(classes.column)}
|
|
93
|
+
data-test-id="dynamic-list-header-cell"
|
|
94
|
+
>
|
|
95
|
+
<p className={clsx(classes.position)}>
|
|
96
|
+
{positionLabel ?? (allowDragging ? 'Position' : 'Pos')}
|
|
97
|
+
</p>
|
|
98
|
+
<div
|
|
99
|
+
className={clsx(
|
|
100
|
+
classes.resizeHandle,
|
|
101
|
+
classes.resizeHandleDisabled,
|
|
102
|
+
)}
|
|
103
|
+
/>
|
|
104
|
+
</div>
|
|
105
|
+
)}
|
|
106
|
+
{columns.map((column, i) => (
|
|
107
|
+
<div
|
|
108
|
+
key={column.key ?? (column.propertyName as string)}
|
|
109
|
+
className={clsx(classes.column)}
|
|
110
|
+
ref={cols[i].ref}
|
|
111
|
+
data-test-id={`dynamic-list-header-property:${
|
|
112
|
+
column.propertyName as string
|
|
113
|
+
}`}
|
|
114
|
+
>
|
|
115
|
+
<p>{column.label}</p>
|
|
116
|
+
<div
|
|
117
|
+
onMouseDown={
|
|
118
|
+
!column.disableResizing
|
|
119
|
+
? (e) => {
|
|
120
|
+
e.preventDefault();
|
|
121
|
+
mouseDown(i);
|
|
122
|
+
}
|
|
123
|
+
: undefined
|
|
124
|
+
}
|
|
125
|
+
onDoubleClick={onResetColumnSizes}
|
|
126
|
+
className={clsx(classes.resizeHandle, {
|
|
127
|
+
[classes.resizeHandleDisabled]: column.disableResizing ?? false,
|
|
128
|
+
})}
|
|
129
|
+
/>
|
|
130
|
+
</div>
|
|
131
|
+
))}
|
|
132
|
+
{showActionColumn && (
|
|
108
133
|
<div
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
? (e) => {
|
|
112
|
-
e.preventDefault();
|
|
113
|
-
mouseDown(i);
|
|
114
|
-
}
|
|
115
|
-
: undefined
|
|
116
|
-
}
|
|
117
|
-
onDoubleClick={onResetColumnSizes}
|
|
118
|
-
className={clsx(classes.resizeHandle, {
|
|
119
|
-
[classes.resizeHandleDisabled]: column.disableResizing ?? false,
|
|
120
|
-
})}
|
|
134
|
+
className={clsx(classes.column)}
|
|
135
|
+
ref={cols[cols.length - 1].ref}
|
|
121
136
|
/>
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
<div className={clsx(classes.column)} ref={cols[cols.length - 1].ref} />
|
|
126
|
-
)}
|
|
127
|
-
</div>
|
|
137
|
+
)}
|
|
138
|
+
</div>
|
|
139
|
+
</>
|
|
128
140
|
);
|
|
129
141
|
};
|
|
@@ -23,14 +23,7 @@ export const BulkEditFormFieldsConfigConverter = (
|
|
|
23
23
|
const keys = Object.keys(config);
|
|
24
24
|
|
|
25
25
|
const FormFields: React.FC = () => {
|
|
26
|
-
const {
|
|
27
|
-
setFieldValue,
|
|
28
|
-
setFieldTouched,
|
|
29
|
-
setErrors,
|
|
30
|
-
errors,
|
|
31
|
-
validateForm,
|
|
32
|
-
values,
|
|
33
|
-
} = useFormikContext<Data>();
|
|
26
|
+
const { setFieldValue, setFieldTouched, values } = useFormikContext<Data>();
|
|
34
27
|
|
|
35
28
|
// Effect to clear empty fields
|
|
36
29
|
// This will set fields with empty strings or empty arrays to undefined
|
|
@@ -46,19 +39,10 @@ export const BulkEditFormFieldsConfigConverter = (
|
|
|
46
39
|
});
|
|
47
40
|
}, [setFieldValue, values]);
|
|
48
41
|
|
|
49
|
-
const onFieldRemoved = (field: string): void => {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
if (errors[field]) {
|
|
54
|
-
// If there was an error for this field, clear it
|
|
55
|
-
const newErrors = { ...errors };
|
|
56
|
-
delete newErrors[field];
|
|
57
|
-
|
|
58
|
-
setErrors(newErrors);
|
|
59
|
-
|
|
60
|
-
validateForm();
|
|
61
|
-
}
|
|
42
|
+
const onFieldRemoved = async (field: string): Promise<void> => {
|
|
43
|
+
// TODO: Update Formik to get the latest types for setFieldValue
|
|
44
|
+
await setFieldValue(field, undefined, false); // setFieldTouched will validate the form
|
|
45
|
+
await setFieldTouched(field, false);
|
|
62
46
|
};
|
|
63
47
|
|
|
64
48
|
const onFieldAdded = (field: string): void => {
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
} from '../FormStation';
|
|
22
22
|
import { IconName } from '../Icons';
|
|
23
23
|
import { ListSelectMode } from '../List';
|
|
24
|
+
import { createConnectionRenderer } from '../Utils';
|
|
24
25
|
import { generateBulkEditMutation } from './BulkEdit/GenerateMutation';
|
|
25
26
|
import { Explorer } from './Explorer';
|
|
26
27
|
import { QuickEditContext } from './QuickEdit/QuickEditContext';
|
|
@@ -34,6 +35,7 @@ interface ExplorerStoryData {
|
|
|
34
35
|
title: string;
|
|
35
36
|
date?: Date;
|
|
36
37
|
desc: string;
|
|
38
|
+
tags?: { nodes: { name: string }[] };
|
|
37
39
|
}
|
|
38
40
|
|
|
39
41
|
type ExplorerStoryType = typeof Explorer<ExplorerStoryData>;
|
|
@@ -120,6 +122,14 @@ const generateData = (
|
|
|
120
122
|
title: `${usePrefix ? `Index ${index}: ` : ''}${faker.random.words(
|
|
121
123
|
faker.datatype.number({ min: 1, max: 3 }),
|
|
122
124
|
)}`,
|
|
125
|
+
tags: {
|
|
126
|
+
nodes: Array.from(
|
|
127
|
+
{ length: faker.datatype.number({ min: 0, max: 10 }) },
|
|
128
|
+
() => ({
|
|
129
|
+
name: faker.random.word(),
|
|
130
|
+
}),
|
|
131
|
+
),
|
|
132
|
+
},
|
|
123
133
|
};
|
|
124
134
|
});
|
|
125
135
|
|
|
@@ -140,6 +150,13 @@ export const Default: StoryObj<ExplorerStoryType> = {
|
|
|
140
150
|
propertyName: 'title',
|
|
141
151
|
label: 'Title',
|
|
142
152
|
},
|
|
153
|
+
{
|
|
154
|
+
propertyName: 'tags',
|
|
155
|
+
label: 'Tags',
|
|
156
|
+
render: createConnectionRenderer<{ nodes: { name: string }[] }>(
|
|
157
|
+
(tag) => tag.name,
|
|
158
|
+
),
|
|
159
|
+
},
|
|
143
160
|
{
|
|
144
161
|
propertyName: 'date',
|
|
145
162
|
label: 'Date',
|
|
@@ -15,24 +15,31 @@ $input-inactive: 1px solid var(--input-border-color, $input-border-color);
|
|
|
15
15
|
border: $input-inactive;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
&.hasValue {
|
|
19
|
+
background: color-mix(
|
|
20
|
+
in srgb,
|
|
21
|
+
var(--filter-background-color, $filter-background-color) 50%,
|
|
22
|
+
transparent
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
18
26
|
.title {
|
|
19
27
|
display: grid;
|
|
20
28
|
grid-template-columns: auto auto;
|
|
21
|
-
grid-template-rows:
|
|
29
|
+
grid-template-rows: 38px;
|
|
22
30
|
place-content: space-between;
|
|
23
31
|
place-items: center;
|
|
24
32
|
color: var(--filter-title-color, $filter-title-color);
|
|
25
33
|
font-size: var(--filter-font-size, $filter-font-size);
|
|
26
|
-
border: 1px solid
|
|
34
|
+
border: 1px solid transparent;
|
|
27
35
|
|
|
28
|
-
transition: border 0.15s ease-in-out 0s;
|
|
29
|
-
transition: background-color 100ms linear;
|
|
36
|
+
transition: border 0.15s ease-in-out 0s, background-color 100ms linear;
|
|
30
37
|
|
|
31
38
|
&:hover {
|
|
32
39
|
border: $border;
|
|
33
40
|
}
|
|
34
41
|
|
|
35
|
-
&.
|
|
42
|
+
&.active {
|
|
36
43
|
background-color: var(
|
|
37
44
|
--filter-background-selected-color,
|
|
38
45
|
$filter-background-selected-color
|
|
@@ -46,13 +53,20 @@ $input-inactive: 1px solid var(--input-border-color, $input-border-color);
|
|
|
46
53
|
var(--input-invalid-border-color, $input-invalid-border-color);
|
|
47
54
|
}
|
|
48
55
|
|
|
49
|
-
&.
|
|
56
|
+
&.hasValue {
|
|
50
57
|
font-weight: bold;
|
|
51
58
|
}
|
|
52
59
|
|
|
53
60
|
.button {
|
|
61
|
+
height: 40px;
|
|
62
|
+
width: 40px;
|
|
63
|
+
|
|
64
|
+
&.rotated svg {
|
|
65
|
+
transform: rotate(90deg);
|
|
66
|
+
}
|
|
67
|
+
|
|
54
68
|
svg {
|
|
55
|
-
|
|
69
|
+
transition: transform 200ms cubic-bezier(0.4, 0, 0.2, 1);
|
|
56
70
|
}
|
|
57
71
|
|
|
58
72
|
svg * {
|
|
@@ -63,7 +77,7 @@ $input-inactive: 1px solid var(--input-border-color, $input-border-color);
|
|
|
63
77
|
}
|
|
64
78
|
}
|
|
65
79
|
|
|
66
|
-
|
|
80
|
+
label {
|
|
67
81
|
padding-left: 15px;
|
|
68
82
|
}
|
|
69
83
|
}
|
|
@@ -78,9 +92,8 @@ $input-inactive: 1px solid var(--input-border-color, $input-border-color);
|
|
|
78
92
|
place-items: center;
|
|
79
93
|
|
|
80
94
|
.button {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
}
|
|
95
|
+
height: 40px;
|
|
96
|
+
width: 40px;
|
|
84
97
|
|
|
85
98
|
svg * {
|
|
86
99
|
stroke: var(
|
|
@@ -92,17 +105,23 @@ $input-inactive: 1px solid var(--input-border-color, $input-border-color);
|
|
|
92
105
|
|
|
93
106
|
span {
|
|
94
107
|
padding-left: 30px;
|
|
95
|
-
padding-bottom: 10px;
|
|
96
108
|
}
|
|
97
109
|
}
|
|
98
110
|
}
|
|
99
111
|
|
|
100
112
|
.content {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
113
|
+
display: grid;
|
|
114
|
+
grid-template-rows: 0fr;
|
|
115
|
+
transition: grid-template-rows 200ms cubic-bezier(0.4, 0, 0.2, 1);
|
|
116
|
+
|
|
117
|
+
&.expanded {
|
|
118
|
+
grid-template-rows: 1fr;
|
|
119
|
+
}
|
|
104
120
|
|
|
105
121
|
.active {
|
|
122
|
+
display: grid;
|
|
123
|
+
overflow: hidden;
|
|
124
|
+
|
|
106
125
|
border-right: $border;
|
|
107
126
|
border-bottom: $border;
|
|
108
127
|
border-left: $border;
|
|
@@ -95,7 +95,7 @@ describe('Filter', () => {
|
|
|
95
95
|
|
|
96
96
|
const filterTitle = wrapper.find('.title');
|
|
97
97
|
|
|
98
|
-
expect(filterTitle.hasClass('
|
|
98
|
+
expect(filterTitle.hasClass('active')).toBe(expected);
|
|
99
99
|
});
|
|
100
100
|
|
|
101
101
|
it('passes active state to custom filter', () => {
|
|
@@ -1,11 +1,5 @@
|
|
|
1
1
|
import clsx from 'clsx';
|
|
2
|
-
import React, {
|
|
3
|
-
PropsWithChildren,
|
|
4
|
-
ReactNode,
|
|
5
|
-
useEffect,
|
|
6
|
-
useRef,
|
|
7
|
-
useState,
|
|
8
|
-
} from 'react';
|
|
2
|
+
import React, { PropsWithChildren, ReactNode, useState } from 'react';
|
|
9
3
|
import { assertNever } from '../../../helpers/utils';
|
|
10
4
|
import { Data } from '../../../types/data';
|
|
11
5
|
import { Button, ButtonContext } from '../../Buttons';
|
|
@@ -58,18 +52,16 @@ export const Filter = <T extends Data>({
|
|
|
58
52
|
}: PropsWithChildren<FilterProps<T>>): JSX.Element => {
|
|
59
53
|
const [isExpanded, setIsExpanded] = useState<boolean>(false);
|
|
60
54
|
const [hasError, setHasError] = useState<boolean>(false);
|
|
61
|
-
const [contentHeight, setContentHeight] = useState<number>(0);
|
|
62
55
|
const [stringValue, setStringValue] = useState<string | undefined>();
|
|
63
56
|
|
|
64
|
-
const
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}, [isExpanded, hasError, value]);
|
|
57
|
+
const inputId = `${String(options.property)}-filter-input`;
|
|
58
|
+
const labelId = `${String(options.property)}-filter-label`;
|
|
59
|
+
const inputBasedFilter = [
|
|
60
|
+
FilterTypes.FreeText,
|
|
61
|
+
FilterTypes.Numeric,
|
|
62
|
+
FilterTypes.Date,
|
|
63
|
+
FilterTypes.DateTime,
|
|
64
|
+
];
|
|
73
65
|
|
|
74
66
|
const onFilterValueChange = (
|
|
75
67
|
prop: keyof T,
|
|
@@ -135,6 +127,7 @@ export const Filter = <T extends Data>({
|
|
|
135
127
|
onError={onError}
|
|
136
128
|
onValidate={onValidate}
|
|
137
129
|
selectOnFocus={selectOnFocus}
|
|
130
|
+
inputId={inputId}
|
|
138
131
|
/>
|
|
139
132
|
);
|
|
140
133
|
|
|
@@ -147,6 +140,7 @@ export const Filter = <T extends Data>({
|
|
|
147
140
|
}
|
|
148
141
|
onError={onError}
|
|
149
142
|
onValidate={onValidate}
|
|
143
|
+
inputId={inputId}
|
|
150
144
|
/>
|
|
151
145
|
);
|
|
152
146
|
|
|
@@ -158,6 +152,7 @@ export const Filter = <T extends Data>({
|
|
|
158
152
|
onSelect={(value: FilterValue) =>
|
|
159
153
|
onFilterValueChange(options.property, value)
|
|
160
154
|
}
|
|
155
|
+
labelId={labelId}
|
|
161
156
|
/>
|
|
162
157
|
);
|
|
163
158
|
|
|
@@ -169,6 +164,7 @@ export const Filter = <T extends Data>({
|
|
|
169
164
|
onSelect={(value: FilterValue) =>
|
|
170
165
|
onFilterValueChange(options.property, value?.toString())
|
|
171
166
|
}
|
|
167
|
+
labelId={labelId}
|
|
172
168
|
/>
|
|
173
169
|
);
|
|
174
170
|
case FilterTypes.SearcheableOptions:
|
|
@@ -181,6 +177,8 @@ export const Filter = <T extends Data>({
|
|
|
181
177
|
onFilterValueChange(options.property, value, stringValue)
|
|
182
178
|
}
|
|
183
179
|
maxItems={options.maxItems}
|
|
180
|
+
inputId={inputId}
|
|
181
|
+
labelId={labelId}
|
|
184
182
|
/>
|
|
185
183
|
);
|
|
186
184
|
|
|
@@ -194,6 +192,7 @@ export const Filter = <T extends Data>({
|
|
|
194
192
|
modifyTime={false}
|
|
195
193
|
onError={onError}
|
|
196
194
|
onValidate={onValidate}
|
|
195
|
+
inputId={inputId}
|
|
197
196
|
/>
|
|
198
197
|
);
|
|
199
198
|
|
|
@@ -207,6 +206,7 @@ export const Filter = <T extends Data>({
|
|
|
207
206
|
modifyTime={true}
|
|
208
207
|
onError={onError}
|
|
209
208
|
onValidate={onValidate}
|
|
209
|
+
inputId={inputId}
|
|
210
210
|
/>
|
|
211
211
|
);
|
|
212
212
|
|
|
@@ -220,6 +220,7 @@ export const Filter = <T extends Data>({
|
|
|
220
220
|
}}
|
|
221
221
|
onError={onError}
|
|
222
222
|
onValidate={onValidate}
|
|
223
|
+
labelId={labelId}
|
|
223
224
|
/>
|
|
224
225
|
);
|
|
225
226
|
|
|
@@ -229,9 +230,16 @@ export const Filter = <T extends Data>({
|
|
|
229
230
|
}
|
|
230
231
|
};
|
|
231
232
|
|
|
233
|
+
const hasValue = value !== undefined && value !== '';
|
|
234
|
+
|
|
232
235
|
return (
|
|
233
236
|
<div
|
|
234
|
-
className={clsx(
|
|
237
|
+
className={clsx(
|
|
238
|
+
classes.container,
|
|
239
|
+
{ [classes.hasValue]: hasValue },
|
|
240
|
+
'filter-container',
|
|
241
|
+
className,
|
|
242
|
+
)}
|
|
235
243
|
data-test-id={`filter:${options.property as string}`}
|
|
236
244
|
data-test-type={FilterTypes[options.type]}
|
|
237
245
|
onClick={() => onFilterClicked()}
|
|
@@ -241,31 +249,36 @@ export const Filter = <T extends Data>({
|
|
|
241
249
|
setIsExpanded(!isExpanded);
|
|
242
250
|
setHasError(false);
|
|
243
251
|
}}
|
|
244
|
-
className={clsx(
|
|
245
|
-
classes.
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
)}
|
|
252
|
+
className={clsx(classes.title, {
|
|
253
|
+
[classes.hasValue]: hasValue,
|
|
254
|
+
[classes.active]: isActive && isExpanded,
|
|
255
|
+
[classes.hasError]: hasError,
|
|
256
|
+
})}
|
|
250
257
|
data-test-id="filter-button-toggle"
|
|
251
258
|
>
|
|
252
|
-
<
|
|
259
|
+
<label
|
|
260
|
+
id={labelId}
|
|
261
|
+
{...(inputBasedFilter.includes(options.type)
|
|
262
|
+
? {}
|
|
263
|
+
: { htmlFor: inputId })}
|
|
264
|
+
data-test-id="filter-label"
|
|
265
|
+
>
|
|
266
|
+
{options.label}
|
|
267
|
+
</label>
|
|
253
268
|
<Button
|
|
254
|
-
icon={IconName.
|
|
255
|
-
className={clsx(classes.button
|
|
269
|
+
icon={IconName.ChevronRight}
|
|
270
|
+
className={clsx(classes.button, {
|
|
271
|
+
[classes.rotated]: isExpanded || hasValue,
|
|
272
|
+
})}
|
|
256
273
|
buttonContext={ButtonContext.None}
|
|
257
274
|
/>
|
|
258
275
|
</div>
|
|
259
276
|
<div
|
|
260
|
-
className={clsx(classes.content)}
|
|
261
|
-
style={{
|
|
262
|
-
maxHeight: contentHeight,
|
|
263
|
-
}}
|
|
277
|
+
className={clsx(classes.content, { [classes.expanded]: isExpanded })}
|
|
264
278
|
>
|
|
265
|
-
{
|
|
279
|
+
{hasValue && !isExpanded && (
|
|
266
280
|
<div
|
|
267
281
|
className={clsx(classes.selectedValue)}
|
|
268
|
-
ref={valueRef}
|
|
269
282
|
onClick={() => {
|
|
270
283
|
setIsExpanded(!isExpanded);
|
|
271
284
|
}}
|
|
@@ -285,7 +298,6 @@ export const Filter = <T extends Data>({
|
|
|
285
298
|
)}
|
|
286
299
|
{isExpanded && (
|
|
287
300
|
<div
|
|
288
|
-
ref={contentRef}
|
|
289
301
|
data-test-id="filter-content"
|
|
290
302
|
className={clsx({ [classes.active]: isActive })}
|
|
291
303
|
>
|
|
@@ -78,6 +78,12 @@ export interface CustomFilterProps {
|
|
|
78
78
|
/** Wether or not the filter is active (default: false) */
|
|
79
79
|
active?: boolean;
|
|
80
80
|
|
|
81
|
+
/**
|
|
82
|
+
* Optional id for the label element.
|
|
83
|
+
* Assign to the label's `id` and reference from the input or group via `aria-labelledby`.
|
|
84
|
+
*/
|
|
85
|
+
labelId?: string;
|
|
86
|
+
|
|
81
87
|
/** Callback triggered when a new filter value is selected */
|
|
82
88
|
onSelect: (
|
|
83
89
|
value: FilterValue,
|
|
@@ -31,6 +31,9 @@ export interface DateTimeFilterProps {
|
|
|
31
31
|
|
|
32
32
|
/** CSS Class name for additional styles */
|
|
33
33
|
className?: string;
|
|
34
|
+
|
|
35
|
+
/** Optional id for the input field */
|
|
36
|
+
inputId?: string;
|
|
34
37
|
}
|
|
35
38
|
|
|
36
39
|
export const DateTimeFilter: React.FC<DateTimeFilterProps> = ({
|
|
@@ -40,6 +43,7 @@ export const DateTimeFilter: React.FC<DateTimeFilterProps> = ({
|
|
|
40
43
|
onError = noop,
|
|
41
44
|
onValidate: customValidate,
|
|
42
45
|
className = '',
|
|
46
|
+
inputId,
|
|
43
47
|
}) => {
|
|
44
48
|
const container = useRef<HTMLDivElement>(null);
|
|
45
49
|
const [showPicker, setShowPicker] = useState(false);
|
|
@@ -111,6 +115,7 @@ export const DateTimeFilter: React.FC<DateTimeFilterProps> = ({
|
|
|
111
115
|
ref={container}
|
|
112
116
|
>
|
|
113
117
|
<input
|
|
118
|
+
id={inputId}
|
|
114
119
|
autoFocus
|
|
115
120
|
className={clsx(classes.inputValue)}
|
|
116
121
|
onKeyDown={handleKeyDown}
|
|
@@ -163,7 +168,7 @@ export const DateTimeFilter: React.FC<DateTimeFilterProps> = ({
|
|
|
163
168
|
onClick={() => {
|
|
164
169
|
setShowPicker(false);
|
|
165
170
|
|
|
166
|
-
if (modifyTime) {
|
|
171
|
+
if (modifyTime && value?.length > 0) {
|
|
167
172
|
const parsedValue = DateTime.fromFormat(value, 'f');
|
|
168
173
|
|
|
169
174
|
if (parsedValue.isValid) {
|
|
@@ -19,6 +19,8 @@ export interface FreeTextFilterProps {
|
|
|
19
19
|
className?: string;
|
|
20
20
|
/** Select text on focus if true (default: true) */
|
|
21
21
|
selectOnFocus?: boolean;
|
|
22
|
+
/** Optional id for the input field */
|
|
23
|
+
inputId?: string;
|
|
22
24
|
}
|
|
23
25
|
|
|
24
26
|
export const FreeTextFilter: React.FC<FreeTextFilterProps> = ({
|
|
@@ -28,6 +30,7 @@ export const FreeTextFilter: React.FC<FreeTextFilterProps> = ({
|
|
|
28
30
|
onValidate: customValidate,
|
|
29
31
|
className = '',
|
|
30
32
|
selectOnFocus = true,
|
|
33
|
+
inputId,
|
|
31
34
|
}) => {
|
|
32
35
|
const [errorMsg, setErrorMsg] = useState<string>();
|
|
33
36
|
const ENTER_KEY = 'Enter';
|
|
@@ -68,6 +71,7 @@ export const FreeTextFilter: React.FC<FreeTextFilterProps> = ({
|
|
|
68
71
|
)}
|
|
69
72
|
>
|
|
70
73
|
<input
|
|
74
|
+
id={inputId}
|
|
71
75
|
ref={inputRef}
|
|
72
76
|
autoFocus
|
|
73
77
|
className={clsx(classes.inputValue, errorMsg && classes.hasError)}
|
|
@@ -17,19 +17,27 @@ export interface MultiOptionFilterProps {
|
|
|
17
17
|
|
|
18
18
|
/** CSS Class name for additional styles */
|
|
19
19
|
className?: string;
|
|
20
|
+
|
|
21
|
+
/** ID for the label */
|
|
22
|
+
labelId?: string;
|
|
20
23
|
}
|
|
21
24
|
|
|
22
25
|
export const MultiOptionsFilter: React.FC<MultiOptionFilterProps> = ({
|
|
23
26
|
value,
|
|
24
27
|
options,
|
|
25
28
|
onSelect,
|
|
29
|
+
labelId,
|
|
26
30
|
}) => {
|
|
27
31
|
const [selectedOptionList, setSelectedOptionList] = useState<string[]>(
|
|
28
32
|
value ? value.split(',') : [],
|
|
29
33
|
);
|
|
30
34
|
|
|
31
35
|
return (
|
|
32
|
-
<div
|
|
36
|
+
<div
|
|
37
|
+
className={classes.multiFilterContainer}
|
|
38
|
+
role="group"
|
|
39
|
+
aria-labelledby={labelId}
|
|
40
|
+
>
|
|
33
41
|
{options?.map((option: Option) => (
|
|
34
42
|
<Checkbox
|
|
35
43
|
className={clsx(
|
|
@@ -18,6 +18,9 @@ export interface NumericTextFilterProps {
|
|
|
18
18
|
|
|
19
19
|
/** CSS Class name for additional styles */
|
|
20
20
|
className?: string;
|
|
21
|
+
|
|
22
|
+
/** Optional id for the input field */
|
|
23
|
+
inputId?: string;
|
|
21
24
|
}
|
|
22
25
|
|
|
23
26
|
export const NumericTextFilter: React.FC<NumericTextFilterProps> = ({
|
|
@@ -26,6 +29,7 @@ export const NumericTextFilter: React.FC<NumericTextFilterProps> = ({
|
|
|
26
29
|
onError = noop,
|
|
27
30
|
onValidate: customValidate,
|
|
28
31
|
className = '',
|
|
32
|
+
inputId,
|
|
29
33
|
}) => {
|
|
30
34
|
const [errorMsg, setErrorMsg] = useState<string>();
|
|
31
35
|
const ENTER_KEY = 'Enter';
|
|
@@ -64,6 +68,7 @@ export const NumericTextFilter: React.FC<NumericTextFilterProps> = ({
|
|
|
64
68
|
)}
|
|
65
69
|
>
|
|
66
70
|
<input
|
|
71
|
+
id={inputId}
|
|
67
72
|
autoFocus
|
|
68
73
|
value={valueLocal}
|
|
69
74
|
className={clsx(classes.inputValue, errorMsg && classes.hasError)}
|