@k-int/stripes-kint-components 5.2.3 → 5.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 +8 -0
- package/es/index.js +8 -0
- package/es/lib/ActionList/ActionListFieldArray.js +27 -14
- package/es/lib/CustomProperties/Config/CustomPropertiesSettings.js +0 -1
- package/es/lib/CustomProperties/Edit/CustomPropertyField.js +0 -1
- package/es/lib/CustomProperties/Filter/CustomPropertiesFilter.js +0 -1
- package/es/lib/CustomProperties/View/CustomPropertiesViewCtx.js +0 -1
- package/es/lib/EditableRefdataCategoryList/EditableRefdataCategoryList.js +50 -9
- package/es/lib/EditableRefdataList/EditableRefdataList.js +0 -1
- package/es/lib/EditableSettingsList/SettingField/EditSettingValue.js +0 -1
- package/es/lib/EditableSettingsList/SettingField/RenderSettingValue.test.js +0 -1
- package/es/lib/RefdataCategoriesSettings/RefdataCategoriesSettings.js +154 -0
- package/es/lib/RefdataCategoriesSettings/index.js +13 -0
- package/es/lib/SettingsFormContainer/SettingsFormContainer.js +0 -1
- package/es/lib/Typedown/Typedown.js +0 -1
- package/es/lib/hooks/__mocks__/index.js +4 -0
- package/es/lib/hooks/index.js +7 -0
- package/es/lib/hooks/useActionListRef.js +34 -0
- package/es/lib/utils/filterParsers/parseKiwtQueryString.js +2 -1
- package/es/lib/utils/parseModConfigEntry.js +0 -1
- package/package.json +1 -1
- package/src/index.js +3 -0
- package/src/lib/ActionList/ActionListFieldArray.js +29 -15
- package/src/lib/EditableRefdataCategoryList/EditableRefdataCategoryList.js +67 -13
- package/src/lib/RefdataCategoriesSettings/RefdataCategoriesSettings.js +176 -0
- package/src/lib/RefdataCategoriesSettings/index.js +1 -0
- package/src/lib/hooks/__mocks__/index.js +4 -0
- package/src/lib/hooks/index.js +1 -0
- package/src/lib/hooks/useActionListRef.js +36 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
# [5.3.0](https://gitlab.com/knowledge-integration/folio/stripes-kint-components/compare/v5.2.3...v5.3.0) (2023-12-04)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* ActionList ([fbf2a30](https://gitlab.com/knowledge-integration/folio/stripes-kint-components/commit/fbf2a3060f1429383489a4ffec2aea243720d1ad))
|
|
7
|
+
* RefdataCategoriesSettings component, improvements to EditableRefdataCategoryList ([a19e503](https://gitlab.com/knowledge-integration/folio/stripes-kint-components/commit/a19e503934b8ff4fbb70aeae0cdcc087fc62a6be))
|
|
8
|
+
|
|
1
9
|
## [5.2.3](https://gitlab.com/knowledge-integration/folio/stripes-kint-components/compare/v5.2.2...v5.2.3) (2023-11-03)
|
|
2
10
|
|
|
3
11
|
|
package/es/index.js
CHANGED
|
@@ -38,6 +38,7 @@ var _exportNames = {
|
|
|
38
38
|
CustomPropertiesFilterFieldArray: true,
|
|
39
39
|
useOperators: true,
|
|
40
40
|
useParseActiveFilterStrings: true,
|
|
41
|
+
RefdataCategoriesSettings: true,
|
|
41
42
|
customPropertyConstants: true,
|
|
42
43
|
endpoints: true,
|
|
43
44
|
comparators: true,
|
|
@@ -212,6 +213,12 @@ Object.defineProperty(exports, "RefdataButtons", {
|
|
|
212
213
|
return _RefdataButtons.default;
|
|
213
214
|
}
|
|
214
215
|
});
|
|
216
|
+
Object.defineProperty(exports, "RefdataCategoriesSettings", {
|
|
217
|
+
enumerable: true,
|
|
218
|
+
get: function () {
|
|
219
|
+
return _RefdataCategoriesSettings.default;
|
|
220
|
+
}
|
|
221
|
+
});
|
|
215
222
|
Object.defineProperty(exports, "ResponsiveButtonGroup", {
|
|
216
223
|
enumerable: true,
|
|
217
224
|
get: function () {
|
|
@@ -372,6 +379,7 @@ var _NoResultsMessage = _interopRequireDefault(require("./lib/NoResultsMessage")
|
|
|
372
379
|
var _RefdataButtons = _interopRequireDefault(require("./lib/RefdataButtons"));
|
|
373
380
|
var _FormModal = _interopRequireDefault(require("./lib/FormModal"));
|
|
374
381
|
var _CustomProperties = require("./lib/CustomProperties");
|
|
382
|
+
var _RefdataCategoriesSettings = _interopRequireDefault(require("./lib/RefdataCategoriesSettings"));
|
|
375
383
|
var _customPropertyConstants = _interopRequireWildcard(require("./lib/constants/customProperties"));
|
|
376
384
|
exports.customPropertyConstants = _customPropertyConstants;
|
|
377
385
|
var _endpoints = _interopRequireWildcard(require("./lib/constants/endpoints"));
|
|
@@ -68,6 +68,7 @@ ActionTrigger.propTypes = {
|
|
|
68
68
|
name: _propTypes.default.string
|
|
69
69
|
})
|
|
70
70
|
};
|
|
71
|
+
const NEW_ROW = 'NEW_ROW';
|
|
71
72
|
const ActionListFieldArray = /*#__PURE__*/(0, _react.forwardRef)((_ref2, ref) => {
|
|
72
73
|
let {
|
|
73
74
|
actionAssigner,
|
|
@@ -107,19 +108,27 @@ const ActionListFieldArray = /*#__PURE__*/(0, _react.forwardRef)((_ref2, ref) =>
|
|
|
107
108
|
null for no field, string id if we are editing an existing field and
|
|
108
109
|
'NEW_ROW' for a new row
|
|
109
110
|
*/
|
|
110
|
-
const [editing, setEditing] = (0, _react.useState)(
|
|
111
|
-
const toggleEditing = id => {
|
|
111
|
+
const [editing, setEditing] = (0, _react.useState)((fields?.value?.filter(f => f._isNewActionListRow)?.length ?? 0) > 0 ? NEW_ROW : undefined);
|
|
112
|
+
const toggleEditing = (0, _react.useCallback)(id => {
|
|
112
113
|
if (editing) {
|
|
113
|
-
setEditing(
|
|
114
|
+
setEditing();
|
|
114
115
|
} else {
|
|
115
116
|
setEditing(id);
|
|
116
117
|
}
|
|
117
|
-
};
|
|
118
|
+
}, [editing]);
|
|
119
|
+
|
|
120
|
+
// Ensure editing doesn't get stuck in "NEW_ROW" state;
|
|
121
|
+
(0, _react.useEffect)(() => {
|
|
122
|
+
if (editing === NEW_ROW && (fields?.value?.filter(f => f._isNewActionListRow)?.length ?? 0) === 0) {
|
|
123
|
+
setEditing();
|
|
124
|
+
}
|
|
125
|
+
}, [editing, fields?.value]);
|
|
118
126
|
const handleSave = index => {
|
|
119
127
|
const {
|
|
120
128
|
actionListActions: _a,
|
|
121
129
|
fieldName: _fn,
|
|
122
130
|
fieldIndex: _fi,
|
|
131
|
+
_isNewActionListRow: _inalr,
|
|
123
132
|
...rowData
|
|
124
133
|
} = fields.value[index];
|
|
125
134
|
|
|
@@ -134,22 +143,26 @@ const ActionListFieldArray = /*#__PURE__*/(0, _react.forwardRef)((_ref2, ref) =>
|
|
|
134
143
|
actionListActions: _a,
|
|
135
144
|
fieldName: _fn,
|
|
136
145
|
fieldIndex: _fi,
|
|
146
|
+
_isNewActionListRow: _inalr,
|
|
137
147
|
...rowData
|
|
138
148
|
} = fields.value[index];
|
|
139
149
|
if (createCallback) {
|
|
140
150
|
createCallback(rowData);
|
|
141
151
|
}
|
|
142
152
|
};
|
|
143
|
-
const handleClickCreate = () => {
|
|
144
|
-
toggleEditing(
|
|
145
|
-
fields.unshift(
|
|
146
|
-
|
|
153
|
+
const handleClickCreate = (0, _react.useCallback)(() => {
|
|
154
|
+
toggleEditing(NEW_ROW);
|
|
155
|
+
fields.unshift({
|
|
156
|
+
...defaultNewObject,
|
|
157
|
+
_isNewActionListRow: true
|
|
158
|
+
});
|
|
159
|
+
}, [defaultNewObject, fields, toggleEditing]);
|
|
147
160
|
|
|
148
161
|
// Way to go into create mode from external component, and way to tell internal editing state
|
|
149
162
|
(0, _react.useImperativeHandle)(ref, () => ({
|
|
150
163
|
create: handleClickCreate,
|
|
151
164
|
editing
|
|
152
|
-
}));
|
|
165
|
+
}), [editing, handleClickCreate]);
|
|
153
166
|
const getColumnWidths = () => {
|
|
154
167
|
const widthNotInUseByActions = editing ? TOTAL_WIDTH - EDITING_ACTIONS_WIDTH : TOTAL_WIDTH - NON_EDITING_ACTIONS_WIDTH;
|
|
155
168
|
const staticWidth = widthNotInUseByActions / visibleFields.length;
|
|
@@ -168,7 +181,7 @@ const ActionListFieldArray = /*#__PURE__*/(0, _react.forwardRef)((_ref2, ref) =>
|
|
|
168
181
|
actionListActions: actions,
|
|
169
182
|
...rest
|
|
170
183
|
} = data;
|
|
171
|
-
if (data.id === editing || !data.id && editing ===
|
|
184
|
+
if (data.id === editing || !data.id && editing === NEW_ROW) {
|
|
172
185
|
// Render the save/cancel buttons
|
|
173
186
|
return /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
|
|
174
187
|
id: `action-button-parent-${data.rowIndex + 1}`,
|
|
@@ -180,12 +193,12 @@ const ActionListFieldArray = /*#__PURE__*/(0, _react.forwardRef)((_ref2, ref) =>
|
|
|
180
193
|
triggerFormSubmit(); // This is set up as () => null in ActionList, so essentially only acts here to force validation
|
|
181
194
|
|
|
182
195
|
if (!hasValidationErrors) {
|
|
183
|
-
if (!data.id && editing ===
|
|
196
|
+
if (!data.id && editing === NEW_ROW) {
|
|
184
197
|
handleCreate(data.rowIndex);
|
|
185
198
|
} else {
|
|
186
199
|
handleSave(data.rowIndex);
|
|
187
200
|
}
|
|
188
|
-
toggleEditing(
|
|
201
|
+
toggleEditing();
|
|
189
202
|
}
|
|
190
203
|
},
|
|
191
204
|
type: "submit",
|
|
@@ -197,9 +210,9 @@ const ActionListFieldArray = /*#__PURE__*/(0, _react.forwardRef)((_ref2, ref) =>
|
|
|
197
210
|
"data-type-button": "cancel",
|
|
198
211
|
marginBottom0: true,
|
|
199
212
|
onClick: () => {
|
|
200
|
-
if (!data.id && editing ===
|
|
213
|
+
if (!data.id && editing === NEW_ROW) {
|
|
201
214
|
fields.remove(data.rowIndex);
|
|
202
|
-
toggleEditing(
|
|
215
|
+
toggleEditing(NEW_ROW);
|
|
203
216
|
} else {
|
|
204
217
|
change(fieldName, (0, _get.default)(initialValues, fieldName));
|
|
205
218
|
toggleEditing(data.id);
|
|
@@ -54,7 +54,6 @@ const CustomPropertiesFilter = _ref => {
|
|
|
54
54
|
]
|
|
55
55
|
}
|
|
56
56
|
});
|
|
57
|
-
|
|
58
57
|
const parsedFilterData = (0, _useParseActiveFilterStrings.default)(custPropFilters || [], passedIntlKey, passedIntlNS, labelOverrides);
|
|
59
58
|
if (isLoading) {
|
|
60
59
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Accordion, {
|
|
@@ -4,18 +4,18 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.default = void 0;
|
|
7
|
-
var _react =
|
|
7
|
+
var _react = require("react");
|
|
8
8
|
var _propTypes = _interopRequireDefault(require("prop-types"));
|
|
9
9
|
var _components = require("@folio/stripes/components");
|
|
10
10
|
var _core = require("@folio/stripes/core");
|
|
11
11
|
var _hooks = require("../hooks");
|
|
12
|
+
var _SearchField = _interopRequireDefault(require("../SearchField"));
|
|
12
13
|
var _ActionList = _interopRequireDefault(require("../ActionList"));
|
|
13
14
|
var _validators = require("../validators");
|
|
14
15
|
var _utils = require("../utils");
|
|
16
|
+
var _CustomProperties = _interopRequireDefault(require("../../../styles/CustomProperties.css"));
|
|
15
17
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
16
18
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
17
|
-
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
18
|
-
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
|
19
19
|
const propTypes = {
|
|
20
20
|
afterQueryCalls: _propTypes.default.object,
|
|
21
21
|
catchQueryCalls: _propTypes.default.object,
|
|
@@ -24,13 +24,17 @@ const propTypes = {
|
|
|
24
24
|
delete: _propTypes.default.bool,
|
|
25
25
|
view: _propTypes.default.bool
|
|
26
26
|
}),
|
|
27
|
+
handleRefdataCategoryClick: _propTypes.default.func,
|
|
28
|
+
hideCreateButton: _propTypes.default.bool,
|
|
27
29
|
intlKey: _propTypes.default.string,
|
|
28
30
|
intlNS: _propTypes.default.string,
|
|
31
|
+
isSearchDisabled: _propTypes.default.bool,
|
|
29
32
|
label: _propTypes.default.oneOfType([_propTypes.default.string, _propTypes.default.node]),
|
|
30
33
|
labelOverrides: _propTypes.default.object,
|
|
34
|
+
onConfirmDelete: _propTypes.default.func,
|
|
31
35
|
refdataEndpoint: _propTypes.default.string
|
|
32
36
|
};
|
|
33
|
-
const EditableRefdataCategoryList = _ref => {
|
|
37
|
+
const EditableRefdataCategoryList = /*#__PURE__*/(0, _react.forwardRef)((_ref, ref) => {
|
|
34
38
|
let {
|
|
35
39
|
afterQueryCalls,
|
|
36
40
|
catchQueryCalls,
|
|
@@ -45,11 +49,17 @@ const EditableRefdataCategoryList = _ref => {
|
|
|
45
49
|
create: true,
|
|
46
50
|
delete: true
|
|
47
51
|
},
|
|
52
|
+
handleRefdataCategoryClick,
|
|
53
|
+
hideCreateButton,
|
|
54
|
+
isSearchDisabled,
|
|
48
55
|
intlKey: passedIntlKey,
|
|
49
56
|
intlNS: passedIntlNS,
|
|
50
57
|
label,
|
|
51
58
|
labelOverrides = {},
|
|
52
59
|
// An object containing translation alternatives
|
|
60
|
+
// A function which will fire on confirmation of delete,
|
|
61
|
+
// with id of deleted Refdata category
|
|
62
|
+
onConfirmDelete = _id => null,
|
|
53
63
|
refdataEndpoint
|
|
54
64
|
} = _ref;
|
|
55
65
|
/* A component that allows for editing of refdata categories */
|
|
@@ -68,6 +78,7 @@ const EditableRefdataCategoryList = _ref => {
|
|
|
68
78
|
endpoint: refdataEndpoint,
|
|
69
79
|
returnQueryObject: true
|
|
70
80
|
});
|
|
81
|
+
const [searchTerm, setSearchTerm] = (0, _react.useState)('');
|
|
71
82
|
const [contentData, setContentData] = (0, _react.useState)([]);
|
|
72
83
|
const [deleteModal, setDeleteModal] = (0, _react.useState)({
|
|
73
84
|
visible: false,
|
|
@@ -76,9 +87,14 @@ const EditableRefdataCategoryList = _ref => {
|
|
|
76
87
|
const sortByDesc = (a, b) => a.desc.localeCompare(b.desc);
|
|
77
88
|
(0, _react.useEffect)(() => {
|
|
78
89
|
if (!isRefdataLoading) {
|
|
79
|
-
|
|
90
|
+
if (searchTerm) {
|
|
91
|
+
const filteredRefdata = refdata?.filter(rd => rd.desc.toLowerCase().includes(searchTerm.toLowerCase()));
|
|
92
|
+
setContentData(filteredRefdata?.sort(sortByDesc) ?? []);
|
|
93
|
+
} else {
|
|
94
|
+
setContentData(refdata?.sort(sortByDesc) ?? []);
|
|
95
|
+
}
|
|
80
96
|
}
|
|
81
|
-
}, [isRefdataLoading, refdata]);
|
|
97
|
+
}, [isRefdataLoading, refdata, searchTerm]);
|
|
82
98
|
|
|
83
99
|
// Edit and Create will use the same POST mutation
|
|
84
100
|
const {
|
|
@@ -115,7 +131,6 @@ const EditableRefdataCategoryList = _ref => {
|
|
|
115
131
|
},
|
|
116
132
|
...catchQueryCalls // override defaults here
|
|
117
133
|
},
|
|
118
|
-
|
|
119
134
|
endpoint: refdataEndpoint,
|
|
120
135
|
id: refdata?.id,
|
|
121
136
|
queryParams: {
|
|
@@ -157,7 +172,21 @@ const EditableRefdataCategoryList = _ref => {
|
|
|
157
172
|
return actionArray;
|
|
158
173
|
};
|
|
159
174
|
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
|
|
160
|
-
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(
|
|
175
|
+
children: [!isSearchDisabled ? /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
|
|
176
|
+
className: _CustomProperties.default.lookupSearchContainer,
|
|
177
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_SearchField.default, {
|
|
178
|
+
ariaLabel: kintIntl.formatKintMessage({
|
|
179
|
+
id: 'refdataCategories.config.searchAriaLabel',
|
|
180
|
+
overrideValue: labelOverrides.searchAriaLabel,
|
|
181
|
+
fallbackMessage: 'refdata-category-search-field'
|
|
182
|
+
}),
|
|
183
|
+
className: _CustomProperties.default.lookupSearch,
|
|
184
|
+
marginBottom0: true,
|
|
185
|
+
onChange: e => setSearchTerm(e.target.value),
|
|
186
|
+
value: searchTerm
|
|
187
|
+
})
|
|
188
|
+
}) : null, /*#__PURE__*/(0, _jsxRuntime.jsx)(_ActionList.default, {
|
|
189
|
+
ref: ref,
|
|
161
190
|
actionAssigner: actionAssigner,
|
|
162
191
|
columnMapping: {
|
|
163
192
|
desc: kintIntl.formatKintMessage({
|
|
@@ -175,10 +204,21 @@ const EditableRefdataCategoryList = _ref => {
|
|
|
175
204
|
},
|
|
176
205
|
createCallback: !createCondition ? null : data => createRefdataCategory(data),
|
|
177
206
|
formatter: {
|
|
207
|
+
desc: rowData => {
|
|
208
|
+
if (handleRefdataCategoryClick) {
|
|
209
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Button, {
|
|
210
|
+
buttonStyle: "link",
|
|
211
|
+
onClick: () => handleRefdataCategoryClick(rowData),
|
|
212
|
+
children: rowData?.desc
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
return rowData?.desc;
|
|
216
|
+
},
|
|
178
217
|
values: rowData => rowData?.values?.length
|
|
179
218
|
}
|
|
180
219
|
/* Hide actions column when no permissions, or no deletable refdata categories */,
|
|
181
220
|
hideActionsColumn: !createCondition && !deleteCondition || !contentData?.find(cd => cd?.values?.length === 0),
|
|
221
|
+
hideCreateButton: hideCreateButton,
|
|
182
222
|
label: label,
|
|
183
223
|
validateFields: {
|
|
184
224
|
desc: () => _validators.required
|
|
@@ -205,6 +245,7 @@ const EditableRefdataCategoryList = _ref => {
|
|
|
205
245
|
}),
|
|
206
246
|
onConfirm: () => {
|
|
207
247
|
deleteRefdataCategory(deleteModal?.refdata?.id);
|
|
248
|
+
onConfirmDelete(deleteModal?.refdata?.id);
|
|
208
249
|
setDeleteModal({
|
|
209
250
|
visible: false,
|
|
210
251
|
refdata: null
|
|
@@ -213,6 +254,6 @@ const EditableRefdataCategoryList = _ref => {
|
|
|
213
254
|
open: deleteModal?.visible
|
|
214
255
|
})]
|
|
215
256
|
});
|
|
216
|
-
};
|
|
257
|
+
});
|
|
217
258
|
EditableRefdataCategoryList.propTypes = propTypes;
|
|
218
259
|
var _default = exports.default = EditableRefdataCategoryList;
|
|
@@ -303,7 +303,6 @@ describe('RenderSettingValue', () => {
|
|
|
303
303
|
expect(getByText('Test template')).toBeInTheDocument(); // TODO this doesn't currently have a [default] marking on it
|
|
304
304
|
});
|
|
305
305
|
});
|
|
306
|
-
|
|
307
306
|
describe('render a template setting without a value or default', () => {
|
|
308
307
|
let renderComponent;
|
|
309
308
|
beforeEach(async () => {
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
var _react = require("react");
|
|
8
|
+
var _propTypes = _interopRequireDefault(require("prop-types"));
|
|
9
|
+
var _components = require("@folio/stripes/components");
|
|
10
|
+
var _hooks = require("../hooks");
|
|
11
|
+
var _EditableRefdataCategoryList = _interopRequireDefault(require("../EditableRefdataCategoryList"));
|
|
12
|
+
var _EditableRefdataList = _interopRequireDefault(require("../EditableRefdataList"));
|
|
13
|
+
var _FormattedKintMessage = _interopRequireDefault(require("../FormattedKintMessage"));
|
|
14
|
+
var _jsxRuntime = require("react/jsx-runtime");
|
|
15
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
16
|
+
const propTypes = {
|
|
17
|
+
afterQueryCalls: _propTypes.default.object,
|
|
18
|
+
catchQueryCalls: _propTypes.default.object,
|
|
19
|
+
displayConditions: _propTypes.default.shape({
|
|
20
|
+
create: _propTypes.default.bool,
|
|
21
|
+
delete: _propTypes.default.bool,
|
|
22
|
+
view: _propTypes.default.bool
|
|
23
|
+
}),
|
|
24
|
+
intlKey: _propTypes.default.string,
|
|
25
|
+
intlNS: _propTypes.default.string,
|
|
26
|
+
label: _propTypes.default.oneOfType([_propTypes.default.string, _propTypes.default.node]),
|
|
27
|
+
labelOverrides: _propTypes.default.object,
|
|
28
|
+
onClose: _propTypes.default.func,
|
|
29
|
+
refdataEndpoint: _propTypes.default.string
|
|
30
|
+
};
|
|
31
|
+
const RefdataCategoriesSettings = _ref => {
|
|
32
|
+
let {
|
|
33
|
+
/*
|
|
34
|
+
* Set of extra booleans for controlling access to actions
|
|
35
|
+
* create/delete (View should be handled externally)
|
|
36
|
+
* This will not overwrite "internal" behaviour, ie setting
|
|
37
|
+
* delete to 'true' here would still not render a delete button
|
|
38
|
+
* for a refdata category that has refdata values.
|
|
39
|
+
*/
|
|
40
|
+
displayConditions = {
|
|
41
|
+
create: true,
|
|
42
|
+
delete: true,
|
|
43
|
+
edit: true
|
|
44
|
+
},
|
|
45
|
+
intlKey: passedIntlKey,
|
|
46
|
+
intlNS: passedIntlNS,
|
|
47
|
+
labelOverrides = {},
|
|
48
|
+
// An object containing translation alternatives
|
|
49
|
+
onClose,
|
|
50
|
+
refdataEndpoint
|
|
51
|
+
} = _ref;
|
|
52
|
+
/* A component that allows for editing of refdata categories */
|
|
53
|
+
const [refdataCategory, setRefdataCategory] = (0, _react.useState)();
|
|
54
|
+
const [refState, rdclRef, passedRef] = (0, _hooks.useActionListRef)();
|
|
55
|
+
const kintIntl = (0, _hooks.useKintIntl)(passedIntlKey, passedIntlNS);
|
|
56
|
+
const handleRefdataCategoryClick = rc => {
|
|
57
|
+
setRefdataCategory(rc);
|
|
58
|
+
};
|
|
59
|
+
const pickListValuesLabel = kintIntl.formatKintMessage({
|
|
60
|
+
id: 'settings.pickListValues',
|
|
61
|
+
overrideValue: labelOverrides?.pickListValues
|
|
62
|
+
});
|
|
63
|
+
const renderViewPaneHeader = renderProps => /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.PaneHeader, {
|
|
64
|
+
...renderProps,
|
|
65
|
+
dismissible: true,
|
|
66
|
+
onClose: () => setRefdataCategory(),
|
|
67
|
+
paneTitle: kintIntl.formatKintMessage({
|
|
68
|
+
id: 'refdataCategories.config.viewPaneTitle',
|
|
69
|
+
overrideValue: labelOverrides?.viewPaneTitle,
|
|
70
|
+
fallbackMessage: refdataCategory?.desc
|
|
71
|
+
}, {
|
|
72
|
+
name: refdataCategory?.desc
|
|
73
|
+
})
|
|
74
|
+
});
|
|
75
|
+
const renderLookupPaneHeader = renderProps => /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.PaneHeader, {
|
|
76
|
+
...renderProps,
|
|
77
|
+
dismissible: true,
|
|
78
|
+
lastMenu: /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.PaneMenu, {
|
|
79
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Button, {
|
|
80
|
+
disabled: !!refState?.editing,
|
|
81
|
+
marginBottom0: true,
|
|
82
|
+
onClick: () => rdclRef?.current?.create(),
|
|
83
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_FormattedKintMessage.default, {
|
|
84
|
+
id: "new"
|
|
85
|
+
})
|
|
86
|
+
})
|
|
87
|
+
}),
|
|
88
|
+
onClose: onClose,
|
|
89
|
+
paneTitle: kintIntl.formatKintMessage({
|
|
90
|
+
id: 'settings.pickLists',
|
|
91
|
+
overrideValue: labelOverrides?.pickLists
|
|
92
|
+
})
|
|
93
|
+
});
|
|
94
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
|
|
95
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Pane, {
|
|
96
|
+
defaultWidth: "fill",
|
|
97
|
+
id: "edit-refdata-desc",
|
|
98
|
+
renderHeader: renderLookupPaneHeader,
|
|
99
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_EditableRefdataCategoryList.default, {
|
|
100
|
+
ref: passedRef,
|
|
101
|
+
afterQueryCalls: {
|
|
102
|
+
post: json => {
|
|
103
|
+
setRefdataCategory(json);
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
displayConditions: displayConditions,
|
|
107
|
+
handleRefdataCategoryClick: handleRefdataCategoryClick,
|
|
108
|
+
hideCreateButton: true,
|
|
109
|
+
onConfirmDelete: id => {
|
|
110
|
+
if (refdataCategory?.id === id) {
|
|
111
|
+
setRefdataCategory();
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
refdataEndpoint: refdataEndpoint
|
|
115
|
+
})
|
|
116
|
+
}), refdataCategory?.desc && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_components.Pane, {
|
|
117
|
+
defaultWidth: "fill",
|
|
118
|
+
id: "settings-refdataCategories-viewPane",
|
|
119
|
+
renderHeader: renderViewPaneHeader,
|
|
120
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_components.Row, {
|
|
121
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Col, {
|
|
122
|
+
xs: 8,
|
|
123
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.KeyValue, {
|
|
124
|
+
label: kintIntl.formatKintMessage({
|
|
125
|
+
id: 'refdataCategory.refdataCategory',
|
|
126
|
+
overrideValue: labelOverrides?.refdataCategory
|
|
127
|
+
}),
|
|
128
|
+
value: refdataCategory.desc
|
|
129
|
+
})
|
|
130
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Col, {
|
|
131
|
+
xs: 4,
|
|
132
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.KeyValue, {
|
|
133
|
+
label: kintIntl.formatKintMessage({
|
|
134
|
+
id: 'refdataCategory.noOfValues',
|
|
135
|
+
overrideValue: labelOverrides?.noOfValues
|
|
136
|
+
}),
|
|
137
|
+
value: refdataCategory?.values?.length
|
|
138
|
+
})
|
|
139
|
+
})]
|
|
140
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_EditableRefdataList.default, {
|
|
141
|
+
afterQueryCalls: {
|
|
142
|
+
put: json => setRefdataCategory(json),
|
|
143
|
+
delete: json => setRefdataCategory(json)
|
|
144
|
+
},
|
|
145
|
+
desc: refdataCategory?.desc,
|
|
146
|
+
displayConditions: displayConditions,
|
|
147
|
+
label: pickListValuesLabel,
|
|
148
|
+
refdataEndpoint: refdataEndpoint
|
|
149
|
+
})]
|
|
150
|
+
})]
|
|
151
|
+
});
|
|
152
|
+
};
|
|
153
|
+
RefdataCategoriesSettings.propTypes = propTypes;
|
|
154
|
+
var _default = exports.default = RefdataCategoriesSettings;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
Object.defineProperty(exports, "default", {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
get: function () {
|
|
9
|
+
return _RefdataCategoriesSettings.default;
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
var _RefdataCategoriesSettings = _interopRequireDefault(require("./RefdataCategoriesSettings"));
|
|
13
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
@@ -79,7 +79,6 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
|
|
|
79
79
|
// At some point we might want to add a catch callout.
|
|
80
80
|
// The current ConfigManager doesn't have one, so leaving for now
|
|
81
81
|
};
|
|
82
|
-
|
|
83
82
|
const getInitialValues = () => {
|
|
84
83
|
let initialValues = parsedSettings;
|
|
85
84
|
if (passedGetInitialValues) {
|
|
@@ -4,10 +4,14 @@ var _refdata = _interopRequireDefault(require("../../../../test/jest/refdata"));
|
|
|
4
4
|
var _customProperties = _interopRequireDefault(require("../../../../test/jest/customProperties"));
|
|
5
5
|
var _useKintIntl = _interopRequireDefault(require("../useKintIntl"));
|
|
6
6
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
7
|
+
/* EXAMPLE Grab actual hooks for anything not mocked here */
|
|
8
|
+
const hooks = jest.requireActual('../index');
|
|
9
|
+
|
|
7
10
|
// We have to do this up here too so that our passed useKintIntl
|
|
8
11
|
// ALSO has a mocked useIntlKeyStore... I think anyway
|
|
9
12
|
jest.mock('../useIntlKeyStore', () => () => () => 'ui-test-implementor');
|
|
10
13
|
module.exports = {
|
|
14
|
+
...hooks,
|
|
11
15
|
useRefdata: () => _refdata.default,
|
|
12
16
|
useTemplates: () => [],
|
|
13
17
|
// We should set up some templates to test this properly
|
package/es/lib/hooks/index.js
CHANGED
|
@@ -3,6 +3,12 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
+
Object.defineProperty(exports, "useActionListRef", {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
get: function () {
|
|
9
|
+
return _useActionListRef.default;
|
|
10
|
+
}
|
|
11
|
+
});
|
|
6
12
|
Object.defineProperty(exports, "useActiveElement", {
|
|
7
13
|
enumerable: true,
|
|
8
14
|
get: function () {
|
|
@@ -136,4 +142,5 @@ var _useIntlKey = _interopRequireDefault(require("./useIntlKey"));
|
|
|
136
142
|
var _useSASQQueryMeta = _interopRequireDefault(require("./useSASQQueryMeta"));
|
|
137
143
|
var _useModConfigEntries = _interopRequireDefault(require("./useModConfigEntries"));
|
|
138
144
|
var _useMutateModConfigEntry = _interopRequireDefault(require("./useMutateModConfigEntry"));
|
|
145
|
+
var _useActionListRef = _interopRequireDefault(require("./useActionListRef"));
|
|
139
146
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
var _react = require("react");
|
|
8
|
+
// Using the passed actionListRef in a stateful way
|
|
9
|
+
// is somewhat complicated, so here's a hook to simplify that work
|
|
10
|
+
const useActionListRef = () => {
|
|
11
|
+
const actionListRef = (0, _react.useRef)();
|
|
12
|
+
const [editing, setEditing] = (0, _react.useState)();
|
|
13
|
+
const passedRef = (0, _react.useCallback)(node => {
|
|
14
|
+
// All functions must be present on here
|
|
15
|
+
actionListRef.current = node;
|
|
16
|
+
if (node?.editing) {
|
|
17
|
+
setEditing(node.editing);
|
|
18
|
+
} else {
|
|
19
|
+
setEditing();
|
|
20
|
+
}
|
|
21
|
+
}, []);
|
|
22
|
+
|
|
23
|
+
// We don't need this for actionListRef, but it serves as an example for building a stateful ref object (sans functions)
|
|
24
|
+
const refState = (0, _react.useMemo)(() => ({
|
|
25
|
+
editing
|
|
26
|
+
}), [editing]);
|
|
27
|
+
return [refState,
|
|
28
|
+
// This is a state containing up to date "editing" state from within ActionList
|
|
29
|
+
actionListRef,
|
|
30
|
+
// This is the ref which will contain the create function
|
|
31
|
+
passedRef // This is the ref to pass to ActionList
|
|
32
|
+
];
|
|
33
|
+
};
|
|
34
|
+
var _default = exports.default = useActionListRef;
|
|
@@ -11,7 +11,8 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
|
|
|
11
11
|
* attribute == value, or attribute isEmpty, or attribute ~= value, etc
|
|
12
12
|
*
|
|
13
13
|
* Will return an object: {path: 'attribute', comparator: '==', value: 'value'}
|
|
14
|
-
*/
|
|
14
|
+
*/
|
|
15
|
+
// No magic numbers sonarlint -.-
|
|
15
16
|
const FIRST = 0;
|
|
16
17
|
const SECOND = 1;
|
|
17
18
|
const THIRD = 2;
|
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -92,6 +92,9 @@ export {
|
|
|
92
92
|
useParseActiveFilterStrings
|
|
93
93
|
} from './lib/CustomProperties';
|
|
94
94
|
|
|
95
|
+
// Refdata categories
|
|
96
|
+
export { default as RefdataCategoriesSettings } from './lib/RefdataCategoriesSettings';
|
|
97
|
+
|
|
95
98
|
export * as customPropertyConstants from './lib/constants/customProperties';
|
|
96
99
|
|
|
97
100
|
export * as endpoints from './lib/constants/endpoints';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { forwardRef, useImperativeHandle, useState } from 'react';
|
|
1
|
+
import React, { forwardRef, useCallback, useEffect, useImperativeHandle, useState } from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
3
|
|
|
4
4
|
import get from 'lodash/get';
|
|
@@ -70,6 +70,8 @@ ActionTrigger.propTypes = {
|
|
|
70
70
|
})
|
|
71
71
|
};
|
|
72
72
|
|
|
73
|
+
const NEW_ROW = 'NEW_ROW';
|
|
74
|
+
|
|
73
75
|
const ActionListFieldArray = forwardRef(({
|
|
74
76
|
actionAssigner,
|
|
75
77
|
columnMapping,
|
|
@@ -100,21 +102,29 @@ const ActionListFieldArray = forwardRef(({
|
|
|
100
102
|
null for no field, string id if we are editing an existing field and
|
|
101
103
|
'NEW_ROW' for a new row
|
|
102
104
|
*/
|
|
103
|
-
const [editing, setEditing] = useState(
|
|
105
|
+
const [editing, setEditing] = useState((fields?.value?.filter(f => f._isNewActionListRow)?.length ?? 0) > 0 ? NEW_ROW : undefined);
|
|
104
106
|
|
|
105
|
-
const toggleEditing = (id) => {
|
|
107
|
+
const toggleEditing = useCallback((id) => {
|
|
106
108
|
if (editing) {
|
|
107
|
-
setEditing(
|
|
109
|
+
setEditing();
|
|
108
110
|
} else {
|
|
109
111
|
setEditing(id);
|
|
110
112
|
}
|
|
111
|
-
};
|
|
113
|
+
}, [editing]);
|
|
114
|
+
|
|
115
|
+
// Ensure editing doesn't get stuck in "NEW_ROW" state;
|
|
116
|
+
useEffect(() => {
|
|
117
|
+
if (editing === NEW_ROW && (fields?.value?.filter(f => f._isNewActionListRow)?.length ?? 0) === 0) {
|
|
118
|
+
setEditing();
|
|
119
|
+
}
|
|
120
|
+
}, [editing, fields?.value]);
|
|
112
121
|
|
|
113
122
|
const handleSave = (index) => {
|
|
114
123
|
const {
|
|
115
124
|
actionListActions: _a,
|
|
116
125
|
fieldName: _fn,
|
|
117
126
|
fieldIndex: _fi,
|
|
127
|
+
_isNewActionListRow: _inalr,
|
|
118
128
|
...rowData
|
|
119
129
|
} = fields.value[index];
|
|
120
130
|
|
|
@@ -130,6 +140,7 @@ const ActionListFieldArray = forwardRef(({
|
|
|
130
140
|
actionListActions: _a,
|
|
131
141
|
fieldName: _fn,
|
|
132
142
|
fieldIndex: _fi,
|
|
143
|
+
_isNewActionListRow: _inalr,
|
|
133
144
|
...rowData
|
|
134
145
|
} = fields.value[index];
|
|
135
146
|
|
|
@@ -138,16 +149,19 @@ const ActionListFieldArray = forwardRef(({
|
|
|
138
149
|
}
|
|
139
150
|
};
|
|
140
151
|
|
|
141
|
-
const handleClickCreate = () => {
|
|
142
|
-
toggleEditing(
|
|
143
|
-
fields.unshift(
|
|
144
|
-
|
|
152
|
+
const handleClickCreate = useCallback(() => {
|
|
153
|
+
toggleEditing(NEW_ROW);
|
|
154
|
+
fields.unshift({
|
|
155
|
+
...defaultNewObject,
|
|
156
|
+
_isNewActionListRow: true
|
|
157
|
+
});
|
|
158
|
+
}, [defaultNewObject, fields, toggleEditing]);
|
|
145
159
|
|
|
146
160
|
// Way to go into create mode from external component, and way to tell internal editing state
|
|
147
161
|
useImperativeHandle(ref, () => ({
|
|
148
162
|
create: handleClickCreate,
|
|
149
163
|
editing
|
|
150
|
-
}));
|
|
164
|
+
}), [editing, handleClickCreate]);
|
|
151
165
|
|
|
152
166
|
const getColumnWidths = () => {
|
|
153
167
|
const widthNotInUseByActions = editing ?
|
|
@@ -173,7 +187,7 @@ const ActionListFieldArray = forwardRef(({
|
|
|
173
187
|
const fieldName = `contentData[${data.rowIndex}]`;
|
|
174
188
|
const { actionListActions: actions, ...rest } = data;
|
|
175
189
|
|
|
176
|
-
if (data.id === editing || (!data.id && editing ===
|
|
190
|
+
if (data.id === editing || (!data.id && editing === NEW_ROW)) {
|
|
177
191
|
// Render the save/cancel buttons
|
|
178
192
|
return (
|
|
179
193
|
<div id={`action-button-parent-${data.rowIndex + 1}`}>
|
|
@@ -186,12 +200,12 @@ const ActionListFieldArray = forwardRef(({
|
|
|
186
200
|
triggerFormSubmit(); // This is set up as () => null in ActionList, so essentially only acts here to force validation
|
|
187
201
|
|
|
188
202
|
if (!hasValidationErrors) {
|
|
189
|
-
if (!data.id && editing ===
|
|
203
|
+
if (!data.id && editing === NEW_ROW) {
|
|
190
204
|
handleCreate(data.rowIndex);
|
|
191
205
|
} else {
|
|
192
206
|
handleSave(data.rowIndex);
|
|
193
207
|
}
|
|
194
|
-
toggleEditing(
|
|
208
|
+
toggleEditing();
|
|
195
209
|
}
|
|
196
210
|
}}
|
|
197
211
|
type="submit"
|
|
@@ -206,9 +220,9 @@ const ActionListFieldArray = forwardRef(({
|
|
|
206
220
|
data-type-button="cancel"
|
|
207
221
|
marginBottom0
|
|
208
222
|
onClick={() => {
|
|
209
|
-
if (!data.id && editing ===
|
|
223
|
+
if (!data.id && editing === NEW_ROW) {
|
|
210
224
|
fields.remove(data.rowIndex);
|
|
211
|
-
toggleEditing(
|
|
225
|
+
toggleEditing(NEW_ROW);
|
|
212
226
|
} else {
|
|
213
227
|
change(fieldName, get(initialValues, fieldName));
|
|
214
228
|
toggleEditing(data.id);
|
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { useEffect, useState, useContext, forwardRef } from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
3
|
|
|
4
|
-
import { ConfirmationModal } from '@folio/stripes/components';
|
|
4
|
+
import { Button, ConfirmationModal } from '@folio/stripes/components';
|
|
5
5
|
import { CalloutContext } from '@folio/stripes/core';
|
|
6
6
|
|
|
7
7
|
import { useKintIntl, useMutateRefdataCategory, useRefdata } from '../hooks';
|
|
8
8
|
|
|
9
|
+
import SearchField from '../SearchField';
|
|
9
10
|
import ActionList from '../ActionList';
|
|
10
11
|
import { required } from '../validators';
|
|
11
12
|
import { parseErrorResponse } from '../utils';
|
|
13
|
+
import css from '../../../styles/CustomProperties.css';
|
|
12
14
|
|
|
13
15
|
const propTypes = {
|
|
14
16
|
afterQueryCalls: PropTypes.object,
|
|
@@ -18,17 +20,21 @@ const propTypes = {
|
|
|
18
20
|
delete: PropTypes.bool,
|
|
19
21
|
view: PropTypes.bool,
|
|
20
22
|
}),
|
|
23
|
+
handleRefdataCategoryClick: PropTypes.func,
|
|
24
|
+
hideCreateButton: PropTypes.bool,
|
|
21
25
|
intlKey: PropTypes.string,
|
|
22
26
|
intlNS: PropTypes.string,
|
|
27
|
+
isSearchDisabled: PropTypes.bool,
|
|
23
28
|
label: PropTypes.oneOfType([
|
|
24
29
|
PropTypes.string,
|
|
25
30
|
PropTypes.node
|
|
26
31
|
]),
|
|
27
32
|
labelOverrides: PropTypes.object,
|
|
28
|
-
|
|
33
|
+
onConfirmDelete: PropTypes.func,
|
|
34
|
+
refdataEndpoint: PropTypes.string,
|
|
29
35
|
};
|
|
30
36
|
|
|
31
|
-
const EditableRefdataCategoryList = ({
|
|
37
|
+
const EditableRefdataCategoryList = forwardRef(({
|
|
32
38
|
afterQueryCalls,
|
|
33
39
|
catchQueryCalls,
|
|
34
40
|
/*
|
|
@@ -42,12 +48,18 @@ const EditableRefdataCategoryList = ({
|
|
|
42
48
|
create: true,
|
|
43
49
|
delete: true,
|
|
44
50
|
},
|
|
51
|
+
handleRefdataCategoryClick,
|
|
52
|
+
hideCreateButton,
|
|
53
|
+
isSearchDisabled,
|
|
45
54
|
intlKey: passedIntlKey,
|
|
46
55
|
intlNS: passedIntlNS,
|
|
47
56
|
label,
|
|
48
57
|
labelOverrides = {}, // An object containing translation alternatives
|
|
49
|
-
|
|
50
|
-
|
|
58
|
+
// A function which will fire on confirmation of delete,
|
|
59
|
+
// with id of deleted Refdata category
|
|
60
|
+
onConfirmDelete = (_id) => null,
|
|
61
|
+
refdataEndpoint,
|
|
62
|
+
}, ref) => {
|
|
51
63
|
/* A component that allows for editing of refdata categories */
|
|
52
64
|
const callout = useContext(CalloutContext);
|
|
53
65
|
const kintIntl = useKintIntl(passedIntlKey, passedIntlNS);
|
|
@@ -63,6 +75,7 @@ const EditableRefdataCategoryList = ({
|
|
|
63
75
|
returnQueryObject: true
|
|
64
76
|
});
|
|
65
77
|
|
|
78
|
+
const [searchTerm, setSearchTerm] = useState('');
|
|
66
79
|
const [contentData, setContentData] = useState([]);
|
|
67
80
|
const [deleteModal, setDeleteModal] = useState({
|
|
68
81
|
visible: false,
|
|
@@ -73,9 +86,14 @@ const EditableRefdataCategoryList = ({
|
|
|
73
86
|
|
|
74
87
|
useEffect(() => {
|
|
75
88
|
if (!isRefdataLoading) {
|
|
76
|
-
|
|
89
|
+
if (searchTerm) {
|
|
90
|
+
const filteredRefdata = refdata?.filter(rd => rd.desc.toLowerCase().includes(searchTerm.toLowerCase()));
|
|
91
|
+
setContentData(filteredRefdata?.sort(sortByDesc) ?? []);
|
|
92
|
+
} else {
|
|
93
|
+
setContentData(refdata?.sort(sortByDesc) ?? []);
|
|
94
|
+
}
|
|
77
95
|
}
|
|
78
|
-
}, [isRefdataLoading, refdata]);
|
|
96
|
+
}, [isRefdataLoading, refdata, searchTerm]);
|
|
79
97
|
|
|
80
98
|
// Edit and Create will use the same POST mutation
|
|
81
99
|
const { delete: deleteRefdataCategory, post: createRefdataCategory } = useMutateRefdataCategory({
|
|
@@ -101,10 +119,10 @@ const EditableRefdataCategoryList = ({
|
|
|
101
119
|
id: 'refdataCategory.deleteRefdataCategory.errorMessage',
|
|
102
120
|
overrideValue: labelOverrides?.deleteError
|
|
103
121
|
},
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
122
|
+
{
|
|
123
|
+
label: deleteModal?.refdata?.label,
|
|
124
|
+
error: errorResp?.message
|
|
125
|
+
}),
|
|
108
126
|
type: 'error',
|
|
109
127
|
});
|
|
110
128
|
},
|
|
@@ -153,7 +171,28 @@ const EditableRefdataCategoryList = ({
|
|
|
153
171
|
|
|
154
172
|
return (
|
|
155
173
|
<>
|
|
174
|
+
{!isSearchDisabled ?
|
|
175
|
+
<div
|
|
176
|
+
className={css.lookupSearchContainer}
|
|
177
|
+
>
|
|
178
|
+
<SearchField
|
|
179
|
+
ariaLabel={
|
|
180
|
+
kintIntl.formatKintMessage({
|
|
181
|
+
id: 'refdataCategories.config.searchAriaLabel',
|
|
182
|
+
overrideValue: labelOverrides.searchAriaLabel,
|
|
183
|
+
fallbackMessage: 'refdata-category-search-field'
|
|
184
|
+
})
|
|
185
|
+
}
|
|
186
|
+
className={css.lookupSearch}
|
|
187
|
+
marginBottom0
|
|
188
|
+
onChange={e => setSearchTerm(e.target.value)}
|
|
189
|
+
value={searchTerm}
|
|
190
|
+
/>
|
|
191
|
+
</div>
|
|
192
|
+
: null
|
|
193
|
+
}
|
|
156
194
|
<ActionList
|
|
195
|
+
ref={ref}
|
|
157
196
|
actionAssigner={actionAssigner}
|
|
158
197
|
columnMapping={{
|
|
159
198
|
desc: kintIntl.formatKintMessage({
|
|
@@ -174,6 +213,19 @@ const EditableRefdataCategoryList = ({
|
|
|
174
213
|
(data) => createRefdataCategory(data)
|
|
175
214
|
}
|
|
176
215
|
formatter={{
|
|
216
|
+
desc: (rowData) => {
|
|
217
|
+
if (handleRefdataCategoryClick) {
|
|
218
|
+
return (
|
|
219
|
+
<Button
|
|
220
|
+
buttonStyle="link"
|
|
221
|
+
onClick={() => handleRefdataCategoryClick(rowData)}
|
|
222
|
+
>
|
|
223
|
+
{rowData?.desc}
|
|
224
|
+
</Button>
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
return rowData?.desc;
|
|
228
|
+
},
|
|
177
229
|
values: (rowData) => rowData?.values?.length
|
|
178
230
|
}}
|
|
179
231
|
/* Hide actions column when no permissions, or no deletable refdata categories */
|
|
@@ -181,6 +233,7 @@ const EditableRefdataCategoryList = ({
|
|
|
181
233
|
(!createCondition && !deleteCondition) ||
|
|
182
234
|
!contentData?.find(cd => cd?.values?.length === 0)
|
|
183
235
|
}
|
|
236
|
+
hideCreateButton={hideCreateButton}
|
|
184
237
|
label={label}
|
|
185
238
|
validateFields={{
|
|
186
239
|
desc: () => required
|
|
@@ -209,13 +262,14 @@ const EditableRefdataCategoryList = ({
|
|
|
209
262
|
onCancel={() => setDeleteModal({ visible: false, refdata: null })}
|
|
210
263
|
onConfirm={() => {
|
|
211
264
|
deleteRefdataCategory(deleteModal?.refdata?.id);
|
|
265
|
+
onConfirmDelete(deleteModal?.refdata?.id);
|
|
212
266
|
setDeleteModal({ visible: false, refdata: null });
|
|
213
267
|
}}
|
|
214
268
|
open={deleteModal?.visible}
|
|
215
269
|
/>
|
|
216
270
|
</>
|
|
217
271
|
);
|
|
218
|
-
};
|
|
272
|
+
});
|
|
219
273
|
|
|
220
274
|
EditableRefdataCategoryList.propTypes = propTypes;
|
|
221
275
|
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
|
|
4
|
+
import { Button, Col, KeyValue, Pane, PaneHeader, PaneMenu, Row } from '@folio/stripes/components';
|
|
5
|
+
|
|
6
|
+
import { useActionListRef, useKintIntl } from '../hooks';
|
|
7
|
+
|
|
8
|
+
import EditableRefdataCategoryList from '../EditableRefdataCategoryList';
|
|
9
|
+
import EditableRefdataList from '../EditableRefdataList';
|
|
10
|
+
import FormattedKintMessage from '../FormattedKintMessage';
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
const propTypes = {
|
|
14
|
+
afterQueryCalls: PropTypes.object,
|
|
15
|
+
catchQueryCalls: PropTypes.object,
|
|
16
|
+
displayConditions: PropTypes.shape({
|
|
17
|
+
create: PropTypes.bool,
|
|
18
|
+
delete: PropTypes.bool,
|
|
19
|
+
view: PropTypes.bool,
|
|
20
|
+
}),
|
|
21
|
+
intlKey: PropTypes.string,
|
|
22
|
+
intlNS: PropTypes.string,
|
|
23
|
+
label: PropTypes.oneOfType([
|
|
24
|
+
PropTypes.string,
|
|
25
|
+
PropTypes.node
|
|
26
|
+
]),
|
|
27
|
+
labelOverrides: PropTypes.object,
|
|
28
|
+
onClose: PropTypes.func,
|
|
29
|
+
refdataEndpoint: PropTypes.string
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const RefdataCategoriesSettings = ({
|
|
33
|
+
/*
|
|
34
|
+
* Set of extra booleans for controlling access to actions
|
|
35
|
+
* create/delete (View should be handled externally)
|
|
36
|
+
* This will not overwrite "internal" behaviour, ie setting
|
|
37
|
+
* delete to 'true' here would still not render a delete button
|
|
38
|
+
* for a refdata category that has refdata values.
|
|
39
|
+
*/
|
|
40
|
+
displayConditions = {
|
|
41
|
+
create: true,
|
|
42
|
+
delete: true,
|
|
43
|
+
edit: true
|
|
44
|
+
},
|
|
45
|
+
intlKey: passedIntlKey,
|
|
46
|
+
intlNS: passedIntlNS,
|
|
47
|
+
labelOverrides = {}, // An object containing translation alternatives
|
|
48
|
+
onClose,
|
|
49
|
+
refdataEndpoint
|
|
50
|
+
}) => {
|
|
51
|
+
/* A component that allows for editing of refdata categories */
|
|
52
|
+
const [refdataCategory, setRefdataCategory] = useState();
|
|
53
|
+
const [refState, rdclRef, passedRef] = useActionListRef();
|
|
54
|
+
|
|
55
|
+
const kintIntl = useKintIntl(passedIntlKey, passedIntlNS);
|
|
56
|
+
|
|
57
|
+
const handleRefdataCategoryClick = (rc) => {
|
|
58
|
+
setRefdataCategory(rc);
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const pickListValuesLabel = kintIntl.formatKintMessage({
|
|
62
|
+
id: 'settings.pickListValues',
|
|
63
|
+
overrideValue: labelOverrides?.pickListValues
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const renderViewPaneHeader = renderProps => (
|
|
67
|
+
<PaneHeader
|
|
68
|
+
{...renderProps}
|
|
69
|
+
dismissible
|
|
70
|
+
onClose={() => setRefdataCategory()}
|
|
71
|
+
paneTitle={
|
|
72
|
+
kintIntl.formatKintMessage(
|
|
73
|
+
{
|
|
74
|
+
id: 'refdataCategories.config.viewPaneTitle',
|
|
75
|
+
overrideValue: labelOverrides?.viewPaneTitle,
|
|
76
|
+
fallbackMessage: refdataCategory?.desc,
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
name: refdataCategory?.desc,
|
|
80
|
+
}
|
|
81
|
+
)
|
|
82
|
+
}
|
|
83
|
+
/>
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
const renderLookupPaneHeader = renderProps => (
|
|
87
|
+
<PaneHeader
|
|
88
|
+
{...renderProps}
|
|
89
|
+
dismissible
|
|
90
|
+
lastMenu={
|
|
91
|
+
<PaneMenu>
|
|
92
|
+
<Button
|
|
93
|
+
disabled={!!refState?.editing}
|
|
94
|
+
marginBottom0
|
|
95
|
+
onClick={() => rdclRef?.current?.create()}
|
|
96
|
+
>
|
|
97
|
+
<FormattedKintMessage id="new" />
|
|
98
|
+
</Button>
|
|
99
|
+
</PaneMenu>
|
|
100
|
+
}
|
|
101
|
+
onClose={onClose}
|
|
102
|
+
paneTitle={kintIntl.formatKintMessage({
|
|
103
|
+
id: 'settings.pickLists',
|
|
104
|
+
overrideValue: labelOverrides?.pickLists
|
|
105
|
+
})}
|
|
106
|
+
/>
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
return (
|
|
110
|
+
<>
|
|
111
|
+
<Pane
|
|
112
|
+
defaultWidth="fill"
|
|
113
|
+
id="edit-refdata-desc"
|
|
114
|
+
renderHeader={renderLookupPaneHeader}
|
|
115
|
+
>
|
|
116
|
+
<EditableRefdataCategoryList
|
|
117
|
+
ref={passedRef}
|
|
118
|
+
afterQueryCalls={{
|
|
119
|
+
post: (json) => { setRefdataCategory(json); }
|
|
120
|
+
}}
|
|
121
|
+
displayConditions={displayConditions}
|
|
122
|
+
handleRefdataCategoryClick={handleRefdataCategoryClick}
|
|
123
|
+
hideCreateButton
|
|
124
|
+
onConfirmDelete={(id) => {
|
|
125
|
+
if (refdataCategory?.id === id) {
|
|
126
|
+
setRefdataCategory();
|
|
127
|
+
}
|
|
128
|
+
}}
|
|
129
|
+
refdataEndpoint={refdataEndpoint}
|
|
130
|
+
/>
|
|
131
|
+
</Pane>
|
|
132
|
+
{refdataCategory?.desc &&
|
|
133
|
+
<Pane
|
|
134
|
+
defaultWidth="fill"
|
|
135
|
+
id="settings-refdataCategories-viewPane"
|
|
136
|
+
renderHeader={renderViewPaneHeader}
|
|
137
|
+
>
|
|
138
|
+
<Row>
|
|
139
|
+
<Col xs={8}>
|
|
140
|
+
<KeyValue
|
|
141
|
+
label={kintIntl.formatKintMessage({
|
|
142
|
+
id: 'refdataCategory.refdataCategory',
|
|
143
|
+
overrideValue: labelOverrides?.refdataCategory
|
|
144
|
+
})}
|
|
145
|
+
value={refdataCategory.desc}
|
|
146
|
+
/>
|
|
147
|
+
</Col>
|
|
148
|
+
<Col xs={4}>
|
|
149
|
+
<KeyValue
|
|
150
|
+
label={kintIntl.formatKintMessage({
|
|
151
|
+
id: 'refdataCategory.noOfValues',
|
|
152
|
+
overrideValue: labelOverrides?.noOfValues
|
|
153
|
+
})}
|
|
154
|
+
value={refdataCategory?.values?.length}
|
|
155
|
+
/>
|
|
156
|
+
</Col>
|
|
157
|
+
</Row>
|
|
158
|
+
<EditableRefdataList
|
|
159
|
+
afterQueryCalls={{
|
|
160
|
+
put: json => setRefdataCategory(json),
|
|
161
|
+
delete: (json) => setRefdataCategory(json)
|
|
162
|
+
}}
|
|
163
|
+
desc={refdataCategory?.desc}
|
|
164
|
+
displayConditions={displayConditions}
|
|
165
|
+
label={pickListValuesLabel}
|
|
166
|
+
refdataEndpoint={refdataEndpoint}
|
|
167
|
+
/>
|
|
168
|
+
</Pane>
|
|
169
|
+
}
|
|
170
|
+
</>
|
|
171
|
+
);
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
RefdataCategoriesSettings.propTypes = propTypes;
|
|
175
|
+
|
|
176
|
+
export default RefdataCategoriesSettings;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './RefdataCategoriesSettings';
|
|
@@ -3,11 +3,15 @@ import customProperties from '../../../../test/jest/customProperties';
|
|
|
3
3
|
|
|
4
4
|
import useKintIntl from '../useKintIntl';
|
|
5
5
|
|
|
6
|
+
/* EXAMPLE Grab actual hooks for anything not mocked here */
|
|
7
|
+
const hooks = jest.requireActual('../index');
|
|
8
|
+
|
|
6
9
|
// We have to do this up here too so that our passed useKintIntl
|
|
7
10
|
// ALSO has a mocked useIntlKeyStore... I think anyway
|
|
8
11
|
jest.mock('../useIntlKeyStore', () => () => () => 'ui-test-implementor');
|
|
9
12
|
|
|
10
13
|
module.exports = {
|
|
14
|
+
...hooks,
|
|
11
15
|
useRefdata: () => refdata,
|
|
12
16
|
useTemplates: () => [], // We should set up some templates to test this properly
|
|
13
17
|
useCustomProperties: () => ({ data: customProperties, isLoading: false }),
|
package/src/lib/hooks/index.js
CHANGED
|
@@ -17,3 +17,4 @@ export { default as useIntlKey } from './useIntlKey';
|
|
|
17
17
|
export { default as useSASQQueryMeta } from './useSASQQueryMeta';
|
|
18
18
|
export { default as useModConfigEntries } from './useModConfigEntries';
|
|
19
19
|
export { default as useMutateModConfigEntry } from './useMutateModConfigEntry';
|
|
20
|
+
export { default as useActionListRef } from './useActionListRef';
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import {
|
|
2
|
+
useCallback,
|
|
3
|
+
useMemo,
|
|
4
|
+
useRef,
|
|
5
|
+
useState
|
|
6
|
+
} from 'react';
|
|
7
|
+
|
|
8
|
+
// Using the passed actionListRef in a stateful way
|
|
9
|
+
// is somewhat complicated, so here's a hook to simplify that work
|
|
10
|
+
const useActionListRef = () => {
|
|
11
|
+
const actionListRef = useRef();
|
|
12
|
+
const [editing, setEditing] = useState();
|
|
13
|
+
|
|
14
|
+
const passedRef = useCallback(node => {
|
|
15
|
+
// All functions must be present on here
|
|
16
|
+
actionListRef.current = node;
|
|
17
|
+
if (node?.editing) {
|
|
18
|
+
setEditing(node.editing);
|
|
19
|
+
} else {
|
|
20
|
+
setEditing();
|
|
21
|
+
}
|
|
22
|
+
}, []);
|
|
23
|
+
|
|
24
|
+
// We don't need this for actionListRef, but it serves as an example for building a stateful ref object (sans functions)
|
|
25
|
+
const refState = useMemo(() => ({
|
|
26
|
+
editing
|
|
27
|
+
}), [editing]);
|
|
28
|
+
|
|
29
|
+
return [
|
|
30
|
+
refState, // This is a state containing up to date "editing" state from within ActionList
|
|
31
|
+
actionListRef, // This is the ref which will contain the create function
|
|
32
|
+
passedRef // This is the ref to pass to ActionList
|
|
33
|
+
];
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export default useActionListRef;
|