@k-int/stripes-kint-components 2.2.0 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -0
- package/es/index.js +124 -4
- package/es/lib/ActionList/ActionList.js +7 -2
- package/es/lib/ActionList/ActionListFieldArray.js +49 -10
- package/es/lib/CustomProperties/Config/CustomPropertiesSettings.js +2 -2
- package/es/lib/CustomProperties/Config/{CustomPropertiesView.js → CustomPropertyView.js} +5 -5
- package/es/lib/CustomProperties/Config/index.js +6 -4
- package/es/lib/CustomProperties/Edit/CustomPropertiesEdit.js +72 -0
- package/es/lib/CustomProperties/Edit/CustomPropertiesEditCtx.js +133 -0
- package/es/lib/CustomProperties/Edit/CustomPropertiesListField.js +279 -0
- package/es/lib/CustomProperties/Edit/CustomPropertyField.js +370 -0
- package/es/lib/CustomProperties/Edit/CustomPropertyFormCard.js +156 -0
- package/es/lib/CustomProperties/Edit/index.js +51 -0
- package/es/lib/CustomProperties/Filter/CustomPropertiesFilter.js +216 -0
- package/es/lib/CustomProperties/Filter/CustomPropertiesFilterField.js +236 -0
- package/es/lib/CustomProperties/Filter/CustomPropertiesFilterFieldArray.js +159 -0
- package/es/lib/CustomProperties/Filter/CustomPropertiesFilterForm.js +119 -0
- package/es/lib/CustomProperties/Filter/CustomPropertiesRule.js +173 -0
- package/es/lib/CustomProperties/Filter/index.js +59 -0
- package/es/lib/CustomProperties/Filter/useOperators.js +138 -0
- package/es/lib/CustomProperties/Filter/useParseActiveFilterStrings.js +97 -0
- package/es/lib/CustomProperties/Filter/useValueProps.js +101 -0
- package/es/lib/CustomProperties/View/CustomPropertiesView.js +73 -0
- package/es/lib/CustomProperties/View/CustomPropertiesViewCtx.js +187 -0
- package/es/lib/CustomProperties/View/CustomPropertyCard.js +204 -0
- package/es/lib/CustomProperties/View/index.js +35 -0
- package/es/lib/CustomProperties/index.js +125 -0
- package/es/lib/EditableRefdataList/EditableRefdataList.js +12 -16
- package/es/lib/FormModal/FormModal.js +18 -4
- package/es/lib/QueryTypedown/QueryTypedown.js +9 -4
- package/es/lib/constants/customProperties.js +4 -1
- package/es/lib/hooks/index.js +16 -0
- package/es/lib/hooks/typedownHooks/useTypedownData.js +9 -2
- package/es/lib/hooks/useAvailableCustomProperties.js +106 -0
- package/es/lib/hooks/useInvalidateRefdata.js +53 -0
- package/es/lib/hooks/useMutateRefdataValue.js +11 -6
- package/es/lib/hooks/useRefdata.js +1 -3
- package/es/lib/utils/groupCustomPropertiesByCtx.js +69 -0
- package/es/lib/utils/index.js +24 -0
- package/es/lib/utils/refdataQueryKey.js +48 -0
- package/es/lib/utils/typedownQueryKey.js +48 -0
- package/es/lib/utils/validators.js +60 -1
- package/git_translate.sh +8 -0
- package/package.json +1 -1
- package/src/index.js +27 -3
- package/src/lib/ActionList/ActionList.js +5 -2
- package/src/lib/ActionList/ActionListFieldArray.js +31 -8
- package/src/lib/ActionList/README.md +23 -20
- package/src/lib/CustomProperties/Config/CustomPropertiesSettings.js +2 -2
- package/src/lib/CustomProperties/Config/{CustomPropertiesView.js → CustomPropertyView.js} +3 -3
- package/src/lib/CustomProperties/Config/index.js +1 -1
- package/src/lib/CustomProperties/Edit/CustomPropertiesEdit.js +35 -0
- package/src/lib/CustomProperties/Edit/CustomPropertiesEditCtx.js +85 -0
- package/src/lib/CustomProperties/Edit/CustomPropertiesListField.js +194 -0
- package/src/lib/CustomProperties/Edit/CustomPropertyField.js +299 -0
- package/src/lib/CustomProperties/Edit/CustomPropertyFormCard.js +131 -0
- package/src/lib/CustomProperties/Edit/index.js +5 -0
- package/src/lib/CustomProperties/Filter/CustomPropertiesFilter.js +125 -0
- package/src/lib/CustomProperties/Filter/CustomPropertiesFilterField.js +148 -0
- package/src/lib/CustomProperties/Filter/CustomPropertiesFilterFieldArray.js +113 -0
- package/src/lib/CustomProperties/Filter/CustomPropertiesFilterForm.js +74 -0
- package/src/lib/CustomProperties/Filter/CustomPropertiesRule.js +122 -0
- package/src/lib/CustomProperties/Filter/index.js +6 -0
- package/src/lib/CustomProperties/Filter/useOperators.js +55 -0
- package/src/lib/CustomProperties/Filter/useParseActiveFilterStrings.js +35 -0
- package/src/lib/CustomProperties/Filter/useValueProps.js +45 -0
- package/src/lib/CustomProperties/View/CustomPropertiesView.js +36 -0
- package/src/lib/CustomProperties/View/CustomPropertiesViewCtx.js +112 -0
- package/src/lib/CustomProperties/View/CustomPropertyCard.js +177 -0
- package/src/lib/CustomProperties/View/index.js +3 -0
- package/src/lib/CustomProperties/index.js +30 -0
- package/src/lib/EditableRefdataList/EditableRefdataList.js +13 -10
- package/src/lib/FormModal/FormModal.js +37 -17
- package/src/lib/QueryTypedown/QueryTypedown.js +3 -1
- package/src/lib/constants/customProperties.js +1 -0
- package/src/lib/hooks/index.js +2 -0
- package/src/lib/hooks/typedownHooks/useTypedownData.js +9 -3
- package/src/lib/hooks/useAvailableCustomProperties.js +40 -0
- package/src/lib/hooks/useInvalidateRefdata.js +11 -0
- package/src/lib/hooks/useMutateRefdataValue.js +7 -3
- package/src/lib/hooks/useRefdata.js +2 -3
- package/src/lib/utils/groupCustomPropertiesByCtx.js +13 -0
- package/src/lib/utils/index.js +5 -0
- package/src/lib/utils/refdataQueryKey.js +9 -0
- package/src/lib/utils/typedownQueryKey.js +9 -0
- package/src/lib/utils/validators.js +40 -0
- package/translate.sh +63 -0
- package/translations/stripes-kint-components/ar.json +105 -0
- package/translations/stripes-kint-components/ca.json +1 -0
- package/translations/stripes-kint-components/cs_CZ.json +105 -0
- package/translations/stripes-kint-components/da.json +1 -0
- package/translations/stripes-kint-components/de.json +105 -0
- package/translations/stripes-kint-components/en.json +54 -2
- package/translations/stripes-kint-components/es.json +105 -0
- package/translations/stripes-kint-components/fr.json +105 -0
- package/translations/stripes-kint-components/he.json +1 -0
- package/translations/stripes-kint-components/hi_IN.json +105 -0
- package/translations/stripes-kint-components/hu.json +105 -0
- package/translations/stripes-kint-components/it_IT.json +105 -0
- package/translations/stripes-kint-components/ja.json +105 -0
- package/translations/stripes-kint-components/ko.json +105 -0
- package/translations/stripes-kint-components/nb.json +1 -0
- package/translations/stripes-kint-components/nn.json +1 -0
- package/translations/stripes-kint-components/pl.json +105 -0
- package/translations/stripes-kint-components/pt_PT.json +105 -0
- package/translations/stripes-kint-components/ru.json +105 -0
- package/translations/stripes-kint-components/sv.json +105 -0
- package/translations/stripes-kint-components/ur.json +1 -0
- package/translations/stripes-kint-components/zh_CN.json +105 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import PropTypes from 'prop-types';
|
|
2
|
+
import { FormattedMessage } from 'react-intl';
|
|
3
|
+
import { Accordion } from '@folio/stripes/components';
|
|
4
|
+
|
|
5
|
+
import CustomPropertiesListField from './CustomPropertiesListField';
|
|
6
|
+
import { useCustomProperties } from '../../hooks';
|
|
7
|
+
|
|
8
|
+
const CustomPropertiesEditCtx = ({
|
|
9
|
+
ctx,
|
|
10
|
+
customPropertiesEndpoint,
|
|
11
|
+
id,
|
|
12
|
+
labelOverrides,
|
|
13
|
+
nameOverride
|
|
14
|
+
}) => {
|
|
15
|
+
// Deal with all the possible label override options
|
|
16
|
+
const getAccordionLabel = () => {
|
|
17
|
+
// Special case for null context
|
|
18
|
+
if (ctx === 'isNull') {
|
|
19
|
+
return (
|
|
20
|
+
labelOverrides.noContext ??
|
|
21
|
+
<FormattedMessage id="stripes-kint-components.customProperties" />
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Label overrides for specific contexts
|
|
26
|
+
if (labelOverrides[ctx]) {
|
|
27
|
+
return labelOverrides[ctx];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Label override for default title, taking ctx into account
|
|
31
|
+
if (labelOverrides.defaultTitle && typeof labelOverrides.defaultTitle === 'function') {
|
|
32
|
+
return (
|
|
33
|
+
labelOverrides.defaultTitle(ctx)
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Label override for default title or finally built in default
|
|
38
|
+
return (
|
|
39
|
+
labelOverrides.defaultTitle ??
|
|
40
|
+
<FormattedMessage id="stripes-kint-components.customProperties.ctx.title" values={{ ctx }} />
|
|
41
|
+
);
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const { data: custprops, isLoading } = useCustomProperties({
|
|
45
|
+
ctx,
|
|
46
|
+
endpoint: customPropertiesEndpoint,
|
|
47
|
+
returnQueryObject: true,
|
|
48
|
+
options: {
|
|
49
|
+
filters: [{
|
|
50
|
+
path: 'retired',
|
|
51
|
+
comparator: '!=',
|
|
52
|
+
value: 'true'
|
|
53
|
+
}]
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
if (isLoading) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return (
|
|
62
|
+
custprops.length > 0 &&
|
|
63
|
+
<Accordion
|
|
64
|
+
id={`${id}-accordion-${ctx}`}
|
|
65
|
+
label={getAccordionLabel()}
|
|
66
|
+
>
|
|
67
|
+
<CustomPropertiesListField
|
|
68
|
+
ctx={ctx}
|
|
69
|
+
customProperties={custprops}
|
|
70
|
+
labelOverrides={labelOverrides}
|
|
71
|
+
name={nameOverride ?? 'customProperties'}
|
|
72
|
+
/>
|
|
73
|
+
</Accordion>
|
|
74
|
+
);
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
CustomPropertiesEditCtx.propTypes = {
|
|
78
|
+
ctx: PropTypes.string,
|
|
79
|
+
customPropertiesEndpoint: PropTypes.string,
|
|
80
|
+
id: PropTypes.string,
|
|
81
|
+
labelOverrides: PropTypes.object,
|
|
82
|
+
nameOverride: PropTypes.string
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export default CustomPropertiesEditCtx;
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { useEffect, useRef, useState } from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import { Field, useFormState } from 'react-final-form';
|
|
4
|
+
|
|
5
|
+
import { Button, KeyValue } from '@folio/stripes/components';
|
|
6
|
+
import { FormattedMessage } from 'react-intl';
|
|
7
|
+
import { useAvailableCustomProperties } from '../../hooks';
|
|
8
|
+
import CustomPropertyFormCard from './CustomPropertyFormCard';
|
|
9
|
+
|
|
10
|
+
const CustomPropertiesList = ({
|
|
11
|
+
availableCustomProperties = [],
|
|
12
|
+
input: {
|
|
13
|
+
name,
|
|
14
|
+
onChange,
|
|
15
|
+
value
|
|
16
|
+
},
|
|
17
|
+
labelOverrides,
|
|
18
|
+
meta: {
|
|
19
|
+
pristine
|
|
20
|
+
},
|
|
21
|
+
}) => {
|
|
22
|
+
const [customProperties, setCustomProperties] = useState([]); // This is the list of customProperties we're currently displaying for edit.
|
|
23
|
+
const [dirtying, setDirtying] = useState(false);
|
|
24
|
+
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
// When the user loads this form, we want to init the list of customProperties
|
|
27
|
+
// we're displaying (state.customProperties) with the list of customProperties that have been set
|
|
28
|
+
// either via defaults or previously-saved data. Since that data may come in
|
|
29
|
+
// _after_ we have mounted this component, we need to check if new data has come in
|
|
30
|
+
// while the form is still marked as pristine.
|
|
31
|
+
//
|
|
32
|
+
// final-form unsets `pristine` after its `onChange` is called, but we also dirty
|
|
33
|
+
// the component when we add/remove rows. That happens _before_ `onChange` is called,
|
|
34
|
+
// so internally we use `state.dirtying` to show that we just initiated an action
|
|
35
|
+
// that will result in a dirty component.
|
|
36
|
+
if (pristine && !dirtying) {
|
|
37
|
+
setCustomProperties(availableCustomProperties.filter(
|
|
38
|
+
customProperty => value[customProperty.value] !== undefined
|
|
39
|
+
));
|
|
40
|
+
}
|
|
41
|
+
return null;
|
|
42
|
+
}, [availableCustomProperties, dirtying, pristine, value]);
|
|
43
|
+
|
|
44
|
+
const handleDeleteCustomProperty = (customProperty, i) => {
|
|
45
|
+
const currentValue = value[customProperty.value]?.[0] ?? {};
|
|
46
|
+
|
|
47
|
+
const newCustomProperties = [...customProperties];
|
|
48
|
+
newCustomProperties.splice(i, 1);
|
|
49
|
+
setCustomProperties(newCustomProperties);
|
|
50
|
+
setDirtying(true);
|
|
51
|
+
|
|
52
|
+
onChange({
|
|
53
|
+
...value,
|
|
54
|
+
[customProperty.value]: [{
|
|
55
|
+
...currentValue,
|
|
56
|
+
_delete: true,
|
|
57
|
+
}],
|
|
58
|
+
});
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const renderCustomProperties = (customPropertyType) => {
|
|
62
|
+
// This is necessary to track individually since "index" will span primary/optional for a given set
|
|
63
|
+
let internalPropertyCounter = 0;
|
|
64
|
+
return customProperties.map((customProperty, index) => {
|
|
65
|
+
if (customPropertyType === 'primary' && !customProperty.primary) return undefined;
|
|
66
|
+
if (customPropertyType === 'optional' && customProperty.primary) return undefined;
|
|
67
|
+
|
|
68
|
+
internalPropertyCounter += 1;
|
|
69
|
+
|
|
70
|
+
return (
|
|
71
|
+
<CustomPropertyFormCard
|
|
72
|
+
key={`customPropertyField-${customProperty.value}-${index}`}
|
|
73
|
+
{...{
|
|
74
|
+
availableCustomProperties,
|
|
75
|
+
customProperty,
|
|
76
|
+
customPropertyType,
|
|
77
|
+
customProperties,
|
|
78
|
+
handleDeleteCustomProperty,
|
|
79
|
+
index,
|
|
80
|
+
internalPropertyCounter,
|
|
81
|
+
labelOverrides,
|
|
82
|
+
name,
|
|
83
|
+
onChange,
|
|
84
|
+
setCustomProperties,
|
|
85
|
+
value
|
|
86
|
+
}}
|
|
87
|
+
/>
|
|
88
|
+
);
|
|
89
|
+
}).filter(cp => cp !== undefined);
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
return (
|
|
93
|
+
<>
|
|
94
|
+
{
|
|
95
|
+
availableCustomProperties.some((customProperty = {}) => customProperty.primary) &&
|
|
96
|
+
<KeyValue
|
|
97
|
+
label={labelOverrides.primaryProperties ?? <FormattedMessage id="stripes-kint-components.customProperties.primaryProperties" />}
|
|
98
|
+
value={renderCustomProperties('primary')}
|
|
99
|
+
/>
|
|
100
|
+
}
|
|
101
|
+
{
|
|
102
|
+
availableCustomProperties.some((customProperty = {}) => !customProperty.primary) &&
|
|
103
|
+
<KeyValue
|
|
104
|
+
label={labelOverrides.optionalProperties ?? <FormattedMessage id="stripes-kint-components.customProperties.optionalProperties" />}
|
|
105
|
+
value={renderCustomProperties('optional')}
|
|
106
|
+
/>
|
|
107
|
+
}
|
|
108
|
+
{
|
|
109
|
+
availableCustomProperties.some((customProperty = {}) => !customProperty.primary) &&
|
|
110
|
+
<Button
|
|
111
|
+
id="add-customproperty-btn"
|
|
112
|
+
onClick={() => {
|
|
113
|
+
setCustomProperties([...customProperties, {}]);
|
|
114
|
+
setDirtying(true);
|
|
115
|
+
}}
|
|
116
|
+
>
|
|
117
|
+
{
|
|
118
|
+
labelOverrides.addProperty ??
|
|
119
|
+
<FormattedMessage id="stripes-kint-components.customProperties.addProperty" />
|
|
120
|
+
}
|
|
121
|
+
</Button>
|
|
122
|
+
}
|
|
123
|
+
</>
|
|
124
|
+
);
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const CustomPropertiesListField = ({
|
|
128
|
+
ctx,
|
|
129
|
+
customProperties,
|
|
130
|
+
labelOverrides,
|
|
131
|
+
...fieldProps
|
|
132
|
+
}) => {
|
|
133
|
+
const fieldRef = useRef();
|
|
134
|
+
const availableCustomProperties = useAvailableCustomProperties(customProperties);
|
|
135
|
+
|
|
136
|
+
const { initialValues } = useFormState();
|
|
137
|
+
const getInitialValue = () => {
|
|
138
|
+
const cps = {};
|
|
139
|
+
(customProperties || [])
|
|
140
|
+
.filter(cp => cp.primary)
|
|
141
|
+
// Change default to be an ignored customProperty.
|
|
142
|
+
// This means any changes without setting the value will be ignored
|
|
143
|
+
.forEach(cp => { cps[cp.name] = [{ _delete: true }]; });
|
|
144
|
+
|
|
145
|
+
// IMPORTANT -- All customproperty ctx sections are adding to the same "initialValue" field
|
|
146
|
+
// Ensure that we don't already have initialValues for this particular set before setting them,
|
|
147
|
+
// to ensure no looping behaviour
|
|
148
|
+
if (Object.keys(cps).every(key => initialValues.customProperties?.[key] !== undefined)) {
|
|
149
|
+
return initialValues.customProperties;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Ensure that if we already had these values in initialvalues they're not overwritten
|
|
153
|
+
return ({ ...cps, ...initialValues.customProperties });
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
return (
|
|
157
|
+
<Field
|
|
158
|
+
{...fieldProps}
|
|
159
|
+
initialValue={getInitialValue()}
|
|
160
|
+
render={p => {
|
|
161
|
+
return (
|
|
162
|
+
<CustomPropertiesList
|
|
163
|
+
ref={fieldRef}
|
|
164
|
+
availableCustomProperties={availableCustomProperties}
|
|
165
|
+
ctx={ctx}
|
|
166
|
+
labelOverrides={labelOverrides}
|
|
167
|
+
{...p}
|
|
168
|
+
/>
|
|
169
|
+
);
|
|
170
|
+
}}
|
|
171
|
+
/>
|
|
172
|
+
);
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
CustomPropertiesListField.propTypes = {
|
|
176
|
+
ctx: PropTypes.string,
|
|
177
|
+
customProperties: PropTypes.arrayOf(PropTypes.object),
|
|
178
|
+
labelOverrides: PropTypes.object,
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
CustomPropertiesList.propTypes = {
|
|
182
|
+
availableCustomProperties: PropTypes.arrayOf(PropTypes.object),
|
|
183
|
+
ctx: PropTypes.string,
|
|
184
|
+
input: PropTypes.shape({
|
|
185
|
+
name: PropTypes.string,
|
|
186
|
+
value: PropTypes.oneOfType([PropTypes.object, PropTypes.string]),
|
|
187
|
+
onChange: PropTypes.func,
|
|
188
|
+
}),
|
|
189
|
+
labelOverrides: PropTypes.object,
|
|
190
|
+
meta: PropTypes.object,
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
export default CustomPropertiesListField;
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
import PropTypes from 'prop-types';
|
|
2
|
+
|
|
3
|
+
import { FormattedMessage, useIntl } from 'react-intl';
|
|
4
|
+
|
|
5
|
+
import { Field } from 'react-final-form';
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
Col,
|
|
9
|
+
Datepicker,
|
|
10
|
+
Row,
|
|
11
|
+
Select,
|
|
12
|
+
TextArea,
|
|
13
|
+
TextField
|
|
14
|
+
} from '@folio/stripes/components';
|
|
15
|
+
|
|
16
|
+
import * as CUSTOM_PROPERTY_TYPES from '../../constants/customProperties';
|
|
17
|
+
|
|
18
|
+
import { customPropertyValidator } from '../../utils/validators';
|
|
19
|
+
|
|
20
|
+
const CustomPropertyField = ({
|
|
21
|
+
availableCustomProperties,
|
|
22
|
+
customProperty,
|
|
23
|
+
customPropertyType,
|
|
24
|
+
customProperties,
|
|
25
|
+
index,
|
|
26
|
+
labelOverrides,
|
|
27
|
+
name,
|
|
28
|
+
onChange,
|
|
29
|
+
value,
|
|
30
|
+
setCustomProperties
|
|
31
|
+
}) => {
|
|
32
|
+
const intl = useIntl();
|
|
33
|
+
|
|
34
|
+
const getCustomProperty = (customPropertyValue) => {
|
|
35
|
+
return availableCustomProperties.find(cp => cp.value === customPropertyValue);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const renderCustomPropertyName = () => {
|
|
39
|
+
const unsetCustomProperties = availableCustomProperties.filter(t => {
|
|
40
|
+
// Only optional properties can be set
|
|
41
|
+
if (t.primary) return false;
|
|
42
|
+
|
|
43
|
+
const custPropValue = value[t.value];
|
|
44
|
+
|
|
45
|
+
// The customProperty is unset and has no value.
|
|
46
|
+
if (custPropValue === undefined) return true;
|
|
47
|
+
|
|
48
|
+
// The customProperty is set but is marked for deletion. Allow reuse.
|
|
49
|
+
if (custPropValue[0]?._delete) return true;
|
|
50
|
+
return false;
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
const customPropertyValue = value[customProperty.value];
|
|
54
|
+
const id = customPropertyValue?.[0]?.id;
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<Select
|
|
58
|
+
autoFocus={!id}
|
|
59
|
+
dataOptions={[customProperty, ...unsetCustomProperties]} // The selected customProperty, and the available unset customProperties
|
|
60
|
+
id={`edit-customproperty-${index}-name`}
|
|
61
|
+
label={
|
|
62
|
+
labelOverrides.name ??
|
|
63
|
+
<FormattedMessage id="stripes-kint-components.customProperty.name" />
|
|
64
|
+
}
|
|
65
|
+
onChange={e => {
|
|
66
|
+
const newValue = e.target.value;
|
|
67
|
+
|
|
68
|
+
// Update `state.customProperties` which controls what customProperties are being edited.
|
|
69
|
+
const newCustomProperties = [...customProperties];
|
|
70
|
+
newCustomProperties[index] = getCustomProperty(newValue);
|
|
71
|
+
setCustomProperties(newCustomProperties);
|
|
72
|
+
|
|
73
|
+
// Update final-form (which tracks what the values for a given customProperty are) because
|
|
74
|
+
// in essence we're deleting a customProperty and creating a new customProperty.
|
|
75
|
+
// We do this by 1) marking the current customProperty for deletion and 2) initing
|
|
76
|
+
// the new customProperty to an empty object.
|
|
77
|
+
const currentValue = value[customProperty.value] ? value[customProperty.value][0] : {};
|
|
78
|
+
onChange({
|
|
79
|
+
...value,
|
|
80
|
+
[customProperty.value]: [{
|
|
81
|
+
id: currentValue.id,
|
|
82
|
+
_delete: true,
|
|
83
|
+
}],
|
|
84
|
+
[newValue]: [{}],
|
|
85
|
+
});
|
|
86
|
+
}}
|
|
87
|
+
required
|
|
88
|
+
value={customProperty.value}
|
|
89
|
+
/>
|
|
90
|
+
);
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const renderCustomPropertyValue = () => {
|
|
94
|
+
const currentValue = value[customProperty.value] ? value[customProperty.value][0] : {};
|
|
95
|
+
const min = Number.MIN_SAFE_INTEGER;
|
|
96
|
+
const max = Number.MAX_SAFE_INTEGER;
|
|
97
|
+
|
|
98
|
+
const handleChange = e => {
|
|
99
|
+
onChange({
|
|
100
|
+
...value,
|
|
101
|
+
[customProperty.value]: [{
|
|
102
|
+
...currentValue,
|
|
103
|
+
_delete: e.target.value === '' ? true : undefined, // Delete customProperty if removing the value.
|
|
104
|
+
value: (customProperty.type === CUSTOM_PROPERTY_TYPES.INTEGER_CLASS_NAME ||
|
|
105
|
+
customProperty.type === CUSTOM_PROPERTY_TYPES.DECIMAL_CLASS_NAME) ?
|
|
106
|
+
parseFloat(e.target.value) : e.target.value // send down the number for integers and decimal types
|
|
107
|
+
}],
|
|
108
|
+
});
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
// Figure out which component we're rendering and specify its unique props.
|
|
112
|
+
let fieldProps;
|
|
113
|
+
|
|
114
|
+
switch (customProperty.type) {
|
|
115
|
+
case CUSTOM_PROPERTY_TYPES.REFDATA_CLASS_NAME:
|
|
116
|
+
fieldProps = {
|
|
117
|
+
component: Select,
|
|
118
|
+
dataOptions: customProperty.options,
|
|
119
|
+
format: (v) => v?.value,
|
|
120
|
+
};
|
|
121
|
+
break;
|
|
122
|
+
case CUSTOM_PROPERTY_TYPES.INTEGER_CLASS_NAME:
|
|
123
|
+
case CUSTOM_PROPERTY_TYPES.DECIMAL_CLASS_NAME:
|
|
124
|
+
fieldProps = {
|
|
125
|
+
badInput: intl.formatMessage({ id: 'stripes-kint-components.errors.customPropertyInvalidNumber' }),
|
|
126
|
+
component: TextField,
|
|
127
|
+
max,
|
|
128
|
+
min,
|
|
129
|
+
rangeOverflow: intl.formatMessage({ id: 'stripes-kint-components.errors.customPropertyDecimalValueNotInRange' }, { min, max }),
|
|
130
|
+
rangeUnderflow: intl.formatMessage({ id: 'stripes-kint-components.errors.customPropertyDecimalValueNotInRange' }, { min, max }),
|
|
131
|
+
step: 'any',
|
|
132
|
+
type: 'number',
|
|
133
|
+
};
|
|
134
|
+
break;
|
|
135
|
+
case CUSTOM_PROPERTY_TYPES.TEXT_CLASS_NAME:
|
|
136
|
+
fieldProps = {
|
|
137
|
+
component: TextArea,
|
|
138
|
+
parse: v => v // Lets us send an empty string instead of `undefined`
|
|
139
|
+
};
|
|
140
|
+
break;
|
|
141
|
+
case CUSTOM_PROPERTY_TYPES.DATE_CLASS_NAME:
|
|
142
|
+
fieldProps = {
|
|
143
|
+
component: Datepicker,
|
|
144
|
+
backendDateStandard: 'YYYY-MM-DD',
|
|
145
|
+
timeZone: 'UTC',
|
|
146
|
+
usePortal: true
|
|
147
|
+
};
|
|
148
|
+
break;
|
|
149
|
+
default:
|
|
150
|
+
fieldProps = { component: TextField };
|
|
151
|
+
break;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return (
|
|
155
|
+
<Field
|
|
156
|
+
data-test-customproperty-value
|
|
157
|
+
id={`edit-customproperty-${index}-value`}
|
|
158
|
+
label={
|
|
159
|
+
labelOverrides.value ??
|
|
160
|
+
<FormattedMessage id="stripes-kint-components.customProperty.value" />
|
|
161
|
+
}
|
|
162
|
+
name={`${name}.${customProperty.value}[0].value`}
|
|
163
|
+
onChange={handleChange}
|
|
164
|
+
required={!customProperty.primary}
|
|
165
|
+
validate={(fieldValue, allValues) => customPropertyValidator(fieldValue, allValues, customProperty)}
|
|
166
|
+
valueMissing={intl.formatMessage({ id: 'stripes-core.label.missingRequiredField' })}
|
|
167
|
+
{...fieldProps}
|
|
168
|
+
/>
|
|
169
|
+
);
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
const renderCustomPropertyVisibility = () => {
|
|
173
|
+
const customPropertyObject = value[customProperty.value]?.[0] ?? {};
|
|
174
|
+
|
|
175
|
+
const handleChange = e => {
|
|
176
|
+
onChange({
|
|
177
|
+
...value,
|
|
178
|
+
[customProperty.value]: [{
|
|
179
|
+
...customPropertyObject,
|
|
180
|
+
internal: e.target.value
|
|
181
|
+
}],
|
|
182
|
+
});
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
return (
|
|
186
|
+
<Select
|
|
187
|
+
data-test-customproperty-visibility
|
|
188
|
+
dataOptions={[
|
|
189
|
+
{ value: true, label: intl.formatMessage({ id: 'stripes-kint-components.customProperty.internalTrue' }) },
|
|
190
|
+
{ value: false, label: intl.formatMessage({ id: 'stripes-kint-components.customProperty.internalFalse' }) }
|
|
191
|
+
]}
|
|
192
|
+
id={`edit-customproperty-${index}-visibility`}
|
|
193
|
+
label={
|
|
194
|
+
labelOverrides.visibility ??
|
|
195
|
+
<FormattedMessage id="stripes-kint-components.customProperty.visibility" />
|
|
196
|
+
}
|
|
197
|
+
onChange={handleChange}
|
|
198
|
+
value={customPropertyObject?.internal ?? customProperty.defaultInternal}
|
|
199
|
+
/>
|
|
200
|
+
);
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
const renderCustomPropertyNoteInternal = () => {
|
|
204
|
+
const customPropertyObject = value[customProperty.value]?.[0] ?? {};
|
|
205
|
+
|
|
206
|
+
const handleChange = e => {
|
|
207
|
+
onChange({
|
|
208
|
+
...value,
|
|
209
|
+
[customProperty.value]: [{
|
|
210
|
+
...customPropertyObject,
|
|
211
|
+
note: e.target.value
|
|
212
|
+
}],
|
|
213
|
+
});
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
return (
|
|
217
|
+
<TextArea
|
|
218
|
+
data-test-customproperty-note
|
|
219
|
+
id={`edit-customproperty-${index}-internal-note`}
|
|
220
|
+
label={
|
|
221
|
+
labelOverrides.internalNote ??
|
|
222
|
+
<FormattedMessage id="stripes-kint-components.customProperty.internalNote" />}
|
|
223
|
+
onChange={handleChange}
|
|
224
|
+
value={customPropertyObject?.note}
|
|
225
|
+
/>
|
|
226
|
+
);
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
const renderCustomPropertyNotePublic = () => {
|
|
230
|
+
const customPropertyObject = value[customProperty.value]?.[0] ?? {};
|
|
231
|
+
|
|
232
|
+
const handleChange = e => {
|
|
233
|
+
onChange({
|
|
234
|
+
...value,
|
|
235
|
+
[customProperty.value]: [{
|
|
236
|
+
...customPropertyObject,
|
|
237
|
+
publicNote: e.target.value
|
|
238
|
+
}],
|
|
239
|
+
});
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
return (
|
|
243
|
+
<TextArea
|
|
244
|
+
data-test-customproperty-public-note
|
|
245
|
+
id={`edit-customproperty-${index}-public-note`}
|
|
246
|
+
label={
|
|
247
|
+
labelOverrides.publicNote ??
|
|
248
|
+
<FormattedMessage id="stripes-kint-components.customProperty.publicNote" />
|
|
249
|
+
}
|
|
250
|
+
onChange={handleChange}
|
|
251
|
+
value={customPropertyObject?.publicNote}
|
|
252
|
+
/>
|
|
253
|
+
);
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
return (
|
|
257
|
+
<>
|
|
258
|
+
{
|
|
259
|
+
customPropertyType === 'optional' &&
|
|
260
|
+
<Row>
|
|
261
|
+
<Col xs={12}>
|
|
262
|
+
{renderCustomPropertyName()}
|
|
263
|
+
</Col>
|
|
264
|
+
</Row>
|
|
265
|
+
}
|
|
266
|
+
<Row>
|
|
267
|
+
<Col md={6} xs={12}>
|
|
268
|
+
{renderCustomPropertyValue()}
|
|
269
|
+
</Col>
|
|
270
|
+
<Col md={6} xs={12}>
|
|
271
|
+
{renderCustomPropertyNoteInternal()}
|
|
272
|
+
</Col>
|
|
273
|
+
</Row>
|
|
274
|
+
<Row>
|
|
275
|
+
<Col md={6} xs={12}>
|
|
276
|
+
{renderCustomPropertyVisibility()}
|
|
277
|
+
</Col>
|
|
278
|
+
<Col md={6} xs={12}>
|
|
279
|
+
{renderCustomPropertyNotePublic()}
|
|
280
|
+
</Col>
|
|
281
|
+
</Row>
|
|
282
|
+
</>
|
|
283
|
+
);
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
CustomPropertyField.propTypes = {
|
|
287
|
+
availableCustomProperties: PropTypes.arrayOf(PropTypes.object),
|
|
288
|
+
customProperty: PropTypes.object,
|
|
289
|
+
customPropertyType: PropTypes.string,
|
|
290
|
+
customProperties: PropTypes.arrayOf(PropTypes.object),
|
|
291
|
+
index: PropTypes.number,
|
|
292
|
+
labelOverrides: PropTypes.object,
|
|
293
|
+
name: PropTypes.string,
|
|
294
|
+
onChange: PropTypes.func,
|
|
295
|
+
setCustomProperties: PropTypes.func,
|
|
296
|
+
value: PropTypes.object
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
export default CustomPropertyField;
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import PropTypes from 'prop-types';
|
|
2
|
+
|
|
3
|
+
import { FormattedMessage } from 'react-intl';
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
Card,
|
|
7
|
+
IconButton,
|
|
8
|
+
InfoPopover,
|
|
9
|
+
Tooltip
|
|
10
|
+
} from '@folio/stripes/components';
|
|
11
|
+
|
|
12
|
+
import CustomPropertyField from './CustomPropertyField';
|
|
13
|
+
|
|
14
|
+
const CustomPropertyFormCard = ({
|
|
15
|
+
availableCustomProperties,
|
|
16
|
+
customProperty,
|
|
17
|
+
customPropertyType,
|
|
18
|
+
customProperties,
|
|
19
|
+
handleDeleteCustomProperty,
|
|
20
|
+
index,
|
|
21
|
+
internalPropertyCounter,
|
|
22
|
+
labelOverrides,
|
|
23
|
+
name,
|
|
24
|
+
onChange,
|
|
25
|
+
setCustomProperties,
|
|
26
|
+
value
|
|
27
|
+
}) => {
|
|
28
|
+
const getHeader = () => {
|
|
29
|
+
if (customPropertyType === 'optional') {
|
|
30
|
+
if (labelOverrides.optionalPropertyHeader && typeof labelOverrides.optionalPropertyHeader === 'function') {
|
|
31
|
+
return labelOverrides.optionalPropertyHeader(internalPropertyCounter);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Label override for optionalPropertyHeader or finally built in default
|
|
35
|
+
return (
|
|
36
|
+
labelOverrides.optionalPropertyHeader ??
|
|
37
|
+
<FormattedMessage
|
|
38
|
+
id="stripes-kint-components.customProperty.optionalPropertyHeader"
|
|
39
|
+
values={{ number: internalPropertyCounter }}
|
|
40
|
+
/>
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Label override for primaryPropertyHeader or finally built in default
|
|
45
|
+
return (
|
|
46
|
+
labelOverrides.primaryPropertyHeader ??
|
|
47
|
+
customProperty.label
|
|
48
|
+
);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const getTooltipText = () => {
|
|
52
|
+
if (labelOverrides.remove && typeof labelOverrides.remove === 'function') {
|
|
53
|
+
return labelOverrides.remove(internalPropertyCounter);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Label override for optionalPropertyHeader or finally built in default
|
|
57
|
+
return (
|
|
58
|
+
labelOverrides.remove ??
|
|
59
|
+
<FormattedMessage
|
|
60
|
+
id="stripes-kint-components.customProperty.remove"
|
|
61
|
+
values={{ number: internalPropertyCounter }}
|
|
62
|
+
/>
|
|
63
|
+
);
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
return (
|
|
67
|
+
<Card
|
|
68
|
+
key={customProperty.value}
|
|
69
|
+
headerEnd={customPropertyType === 'optional' ?
|
|
70
|
+
<Tooltip
|
|
71
|
+
id={`customProperty-${customProperty.value}-${index}`}
|
|
72
|
+
text={getTooltipText()}
|
|
73
|
+
>
|
|
74
|
+
{({ ref, ariaIds }) => (
|
|
75
|
+
<IconButton
|
|
76
|
+
ref={ref}
|
|
77
|
+
aria-labelledby={ariaIds.text}
|
|
78
|
+
icon="trash"
|
|
79
|
+
id={`edit-customproperty-${index}-delete`}
|
|
80
|
+
onClick={() => handleDeleteCustomProperty(customProperty, index)}
|
|
81
|
+
/>
|
|
82
|
+
)}
|
|
83
|
+
</Tooltip> :
|
|
84
|
+
null
|
|
85
|
+
}
|
|
86
|
+
headerStart={
|
|
87
|
+
<strong>
|
|
88
|
+
{getHeader()}
|
|
89
|
+
{customProperty.description ? (
|
|
90
|
+
<InfoPopover
|
|
91
|
+
content={customProperty.description}
|
|
92
|
+
/>
|
|
93
|
+
) : null}
|
|
94
|
+
</strong>
|
|
95
|
+
}
|
|
96
|
+
>
|
|
97
|
+
<CustomPropertyField
|
|
98
|
+
{...{
|
|
99
|
+
availableCustomProperties,
|
|
100
|
+
customProperty,
|
|
101
|
+
customPropertyType,
|
|
102
|
+
customProperties,
|
|
103
|
+
index,
|
|
104
|
+
internalPropertyCounter,
|
|
105
|
+
labelOverrides,
|
|
106
|
+
name,
|
|
107
|
+
onChange,
|
|
108
|
+
setCustomProperties,
|
|
109
|
+
value
|
|
110
|
+
}}
|
|
111
|
+
/>
|
|
112
|
+
</Card>
|
|
113
|
+
);
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
CustomPropertyFormCard.propTypes = {
|
|
117
|
+
availableCustomProperties: PropTypes.arrayOf(PropTypes.object),
|
|
118
|
+
customProperty: PropTypes.object,
|
|
119
|
+
customPropertyType: PropTypes.string,
|
|
120
|
+
customProperties: PropTypes.arrayOf(PropTypes.object),
|
|
121
|
+
handleDeleteCustomProperty: PropTypes.func,
|
|
122
|
+
index: PropTypes.number,
|
|
123
|
+
internalPropertyCounter: PropTypes.number,
|
|
124
|
+
labelOverrides: PropTypes.object,
|
|
125
|
+
name: PropTypes.string,
|
|
126
|
+
onChange: PropTypes.func,
|
|
127
|
+
setCustomProperties: PropTypes.func,
|
|
128
|
+
value: PropTypes.object
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
export default CustomPropertyFormCard;
|