@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.
Files changed (109) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/es/index.js +124 -4
  3. package/es/lib/ActionList/ActionList.js +7 -2
  4. package/es/lib/ActionList/ActionListFieldArray.js +49 -10
  5. package/es/lib/CustomProperties/Config/CustomPropertiesSettings.js +2 -2
  6. package/es/lib/CustomProperties/Config/{CustomPropertiesView.js → CustomPropertyView.js} +5 -5
  7. package/es/lib/CustomProperties/Config/index.js +6 -4
  8. package/es/lib/CustomProperties/Edit/CustomPropertiesEdit.js +72 -0
  9. package/es/lib/CustomProperties/Edit/CustomPropertiesEditCtx.js +133 -0
  10. package/es/lib/CustomProperties/Edit/CustomPropertiesListField.js +279 -0
  11. package/es/lib/CustomProperties/Edit/CustomPropertyField.js +370 -0
  12. package/es/lib/CustomProperties/Edit/CustomPropertyFormCard.js +156 -0
  13. package/es/lib/CustomProperties/Edit/index.js +51 -0
  14. package/es/lib/CustomProperties/Filter/CustomPropertiesFilter.js +216 -0
  15. package/es/lib/CustomProperties/Filter/CustomPropertiesFilterField.js +236 -0
  16. package/es/lib/CustomProperties/Filter/CustomPropertiesFilterFieldArray.js +159 -0
  17. package/es/lib/CustomProperties/Filter/CustomPropertiesFilterForm.js +119 -0
  18. package/es/lib/CustomProperties/Filter/CustomPropertiesRule.js +173 -0
  19. package/es/lib/CustomProperties/Filter/index.js +59 -0
  20. package/es/lib/CustomProperties/Filter/useOperators.js +138 -0
  21. package/es/lib/CustomProperties/Filter/useParseActiveFilterStrings.js +97 -0
  22. package/es/lib/CustomProperties/Filter/useValueProps.js +101 -0
  23. package/es/lib/CustomProperties/View/CustomPropertiesView.js +73 -0
  24. package/es/lib/CustomProperties/View/CustomPropertiesViewCtx.js +187 -0
  25. package/es/lib/CustomProperties/View/CustomPropertyCard.js +204 -0
  26. package/es/lib/CustomProperties/View/index.js +35 -0
  27. package/es/lib/CustomProperties/index.js +125 -0
  28. package/es/lib/EditableRefdataList/EditableRefdataList.js +12 -16
  29. package/es/lib/FormModal/FormModal.js +18 -4
  30. package/es/lib/QueryTypedown/QueryTypedown.js +9 -4
  31. package/es/lib/constants/customProperties.js +4 -1
  32. package/es/lib/hooks/index.js +16 -0
  33. package/es/lib/hooks/typedownHooks/useTypedownData.js +9 -2
  34. package/es/lib/hooks/useAvailableCustomProperties.js +106 -0
  35. package/es/lib/hooks/useInvalidateRefdata.js +53 -0
  36. package/es/lib/hooks/useMutateRefdataValue.js +11 -6
  37. package/es/lib/hooks/useRefdata.js +1 -3
  38. package/es/lib/utils/groupCustomPropertiesByCtx.js +69 -0
  39. package/es/lib/utils/index.js +24 -0
  40. package/es/lib/utils/refdataQueryKey.js +48 -0
  41. package/es/lib/utils/typedownQueryKey.js +48 -0
  42. package/es/lib/utils/validators.js +60 -1
  43. package/git_translate.sh +8 -0
  44. package/package.json +1 -1
  45. package/src/index.js +27 -3
  46. package/src/lib/ActionList/ActionList.js +5 -2
  47. package/src/lib/ActionList/ActionListFieldArray.js +31 -8
  48. package/src/lib/ActionList/README.md +23 -20
  49. package/src/lib/CustomProperties/Config/CustomPropertiesSettings.js +2 -2
  50. package/src/lib/CustomProperties/Config/{CustomPropertiesView.js → CustomPropertyView.js} +3 -3
  51. package/src/lib/CustomProperties/Config/index.js +1 -1
  52. package/src/lib/CustomProperties/Edit/CustomPropertiesEdit.js +35 -0
  53. package/src/lib/CustomProperties/Edit/CustomPropertiesEditCtx.js +85 -0
  54. package/src/lib/CustomProperties/Edit/CustomPropertiesListField.js +194 -0
  55. package/src/lib/CustomProperties/Edit/CustomPropertyField.js +299 -0
  56. package/src/lib/CustomProperties/Edit/CustomPropertyFormCard.js +131 -0
  57. package/src/lib/CustomProperties/Edit/index.js +5 -0
  58. package/src/lib/CustomProperties/Filter/CustomPropertiesFilter.js +125 -0
  59. package/src/lib/CustomProperties/Filter/CustomPropertiesFilterField.js +148 -0
  60. package/src/lib/CustomProperties/Filter/CustomPropertiesFilterFieldArray.js +113 -0
  61. package/src/lib/CustomProperties/Filter/CustomPropertiesFilterForm.js +74 -0
  62. package/src/lib/CustomProperties/Filter/CustomPropertiesRule.js +122 -0
  63. package/src/lib/CustomProperties/Filter/index.js +6 -0
  64. package/src/lib/CustomProperties/Filter/useOperators.js +55 -0
  65. package/src/lib/CustomProperties/Filter/useParseActiveFilterStrings.js +35 -0
  66. package/src/lib/CustomProperties/Filter/useValueProps.js +45 -0
  67. package/src/lib/CustomProperties/View/CustomPropertiesView.js +36 -0
  68. package/src/lib/CustomProperties/View/CustomPropertiesViewCtx.js +112 -0
  69. package/src/lib/CustomProperties/View/CustomPropertyCard.js +177 -0
  70. package/src/lib/CustomProperties/View/index.js +3 -0
  71. package/src/lib/CustomProperties/index.js +30 -0
  72. package/src/lib/EditableRefdataList/EditableRefdataList.js +13 -10
  73. package/src/lib/FormModal/FormModal.js +37 -17
  74. package/src/lib/QueryTypedown/QueryTypedown.js +3 -1
  75. package/src/lib/constants/customProperties.js +1 -0
  76. package/src/lib/hooks/index.js +2 -0
  77. package/src/lib/hooks/typedownHooks/useTypedownData.js +9 -3
  78. package/src/lib/hooks/useAvailableCustomProperties.js +40 -0
  79. package/src/lib/hooks/useInvalidateRefdata.js +11 -0
  80. package/src/lib/hooks/useMutateRefdataValue.js +7 -3
  81. package/src/lib/hooks/useRefdata.js +2 -3
  82. package/src/lib/utils/groupCustomPropertiesByCtx.js +13 -0
  83. package/src/lib/utils/index.js +5 -0
  84. package/src/lib/utils/refdataQueryKey.js +9 -0
  85. package/src/lib/utils/typedownQueryKey.js +9 -0
  86. package/src/lib/utils/validators.js +40 -0
  87. package/translate.sh +63 -0
  88. package/translations/stripes-kint-components/ar.json +105 -0
  89. package/translations/stripes-kint-components/ca.json +1 -0
  90. package/translations/stripes-kint-components/cs_CZ.json +105 -0
  91. package/translations/stripes-kint-components/da.json +1 -0
  92. package/translations/stripes-kint-components/de.json +105 -0
  93. package/translations/stripes-kint-components/en.json +54 -2
  94. package/translations/stripes-kint-components/es.json +105 -0
  95. package/translations/stripes-kint-components/fr.json +105 -0
  96. package/translations/stripes-kint-components/he.json +1 -0
  97. package/translations/stripes-kint-components/hi_IN.json +105 -0
  98. package/translations/stripes-kint-components/hu.json +105 -0
  99. package/translations/stripes-kint-components/it_IT.json +105 -0
  100. package/translations/stripes-kint-components/ja.json +105 -0
  101. package/translations/stripes-kint-components/ko.json +105 -0
  102. package/translations/stripes-kint-components/nb.json +1 -0
  103. package/translations/stripes-kint-components/nn.json +1 -0
  104. package/translations/stripes-kint-components/pl.json +105 -0
  105. package/translations/stripes-kint-components/pt_PT.json +105 -0
  106. package/translations/stripes-kint-components/ru.json +105 -0
  107. package/translations/stripes-kint-components/sv.json +105 -0
  108. package/translations/stripes-kint-components/ur.json +1 -0
  109. 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;