@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.
Files changed (29) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/es/index.js +8 -0
  3. package/es/lib/ActionList/ActionListFieldArray.js +27 -14
  4. package/es/lib/CustomProperties/Config/CustomPropertiesSettings.js +0 -1
  5. package/es/lib/CustomProperties/Edit/CustomPropertyField.js +0 -1
  6. package/es/lib/CustomProperties/Filter/CustomPropertiesFilter.js +0 -1
  7. package/es/lib/CustomProperties/View/CustomPropertiesViewCtx.js +0 -1
  8. package/es/lib/EditableRefdataCategoryList/EditableRefdataCategoryList.js +50 -9
  9. package/es/lib/EditableRefdataList/EditableRefdataList.js +0 -1
  10. package/es/lib/EditableSettingsList/SettingField/EditSettingValue.js +0 -1
  11. package/es/lib/EditableSettingsList/SettingField/RenderSettingValue.test.js +0 -1
  12. package/es/lib/RefdataCategoriesSettings/RefdataCategoriesSettings.js +154 -0
  13. package/es/lib/RefdataCategoriesSettings/index.js +13 -0
  14. package/es/lib/SettingsFormContainer/SettingsFormContainer.js +0 -1
  15. package/es/lib/Typedown/Typedown.js +0 -1
  16. package/es/lib/hooks/__mocks__/index.js +4 -0
  17. package/es/lib/hooks/index.js +7 -0
  18. package/es/lib/hooks/useActionListRef.js +34 -0
  19. package/es/lib/utils/filterParsers/parseKiwtQueryString.js +2 -1
  20. package/es/lib/utils/parseModConfigEntry.js +0 -1
  21. package/package.json +1 -1
  22. package/src/index.js +3 -0
  23. package/src/lib/ActionList/ActionListFieldArray.js +29 -15
  24. package/src/lib/EditableRefdataCategoryList/EditableRefdataCategoryList.js +67 -13
  25. package/src/lib/RefdataCategoriesSettings/RefdataCategoriesSettings.js +176 -0
  26. package/src/lib/RefdataCategoriesSettings/index.js +1 -0
  27. package/src/lib/hooks/__mocks__/index.js +4 -0
  28. package/src/lib/hooks/index.js +1 -0
  29. 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)(null);
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(null);
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('NEW_ROW');
145
- fields.unshift(defaultNewObject);
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 === 'NEW_ROW') {
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 === 'NEW_ROW') {
196
+ if (!data.id && editing === NEW_ROW) {
184
197
  handleCreate(data.rowIndex);
185
198
  } else {
186
199
  handleSave(data.rowIndex);
187
200
  }
188
- toggleEditing(data.id);
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 === 'NEW_ROW') {
213
+ if (!data.id && editing === NEW_ROW) {
201
214
  fields.remove(data.rowIndex);
202
- toggleEditing('NEW_ROW');
215
+ toggleEditing(NEW_ROW);
203
216
  } else {
204
217
  change(fieldName, (0, _get.default)(initialValues, fieldName));
205
218
  toggleEditing(data.id);
@@ -121,7 +121,6 @@ const CustomPropertiesSettings = _ref => {
121
121
  },
122
122
  ...catchQueryCalls // Override defaults here
123
123
  },
124
-
125
124
  endpoint: customPropertiesEndpoint,
126
125
  id: customProperty?.id
127
126
  });
@@ -202,7 +202,6 @@ const CustomPropertyField = _ref => {
202
202
  component: _components.TextArea,
203
203
  parse: v => v // Lets us send an empty string instead of `undefined`
204
204
  };
205
-
206
205
  break;
207
206
  case CUSTOM_PROPERTY_TYPES.DATE_CLASS_NAME:
208
207
  fieldProps = {
@@ -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, {
@@ -74,7 +74,6 @@ const CustomPropertiesViewCtx = _ref => {
74
74
  ]
75
75
  }
76
76
  });
77
-
78
77
  if (isLoading) {
79
78
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Accordion, {
80
79
  closedByDefault: true,
@@ -4,18 +4,18 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.default = void 0;
7
- var _react = _interopRequireWildcard(require("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
- setContentData(refdata?.sort(sortByDesc) ?? []);
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)(_ActionList.default, {
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;
@@ -128,7 +128,6 @@ const EditableRefdataList = _ref => {
128
128
  },
129
129
  ...catchQueryCalls // override defaults here
130
130
  },
131
-
132
131
  endpoint: refdataEndpoint,
133
132
  id: refdata?.id,
134
133
  queryParams: {
@@ -83,7 +83,6 @@ const EditSettingValue = props => {
83
83
  });
84
84
  }
85
85
  };
86
-
87
86
  EditSettingValue.propTypes = {
88
87
  currentSetting: _propTypes.default.shape({
89
88
  settingType: _propTypes.default.string,
@@ -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) {
@@ -174,7 +174,6 @@ const Typedown = _ref => {
174
174
  e.stopPropagation();
175
175
  } // prevent propagation of click events
176
176
  },
177
-
178
177
  overlayRef: overlayRef,
179
178
  portal: portal,
180
179
  children: dropDown()
@@ -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
@@ -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
- */ // No magic numbers sonarlint -.-
14
+ */
15
+ // No magic numbers sonarlint -.-
15
16
  const FIRST = 0;
16
17
  const SECOND = 1;
17
18
  const THIRD = 2;
@@ -14,7 +14,6 @@ const parseModConfigEntry = setting => {
14
14
  console.error(error); // eslint-disable-line no-console
15
15
  }
16
16
  }
17
-
18
17
  return parsedSettings;
19
18
  };
20
19
  var _default = exports.default = parseModConfigEntry;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@k-int/stripes-kint-components",
3
- "version": "5.2.3",
3
+ "version": "5.3.0",
4
4
  "description": "Stripes Component library for K-Int specific applications",
5
5
  "sideEffects": [
6
6
  "*.css"
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(null);
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(null);
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('NEW_ROW');
143
- fields.unshift(defaultNewObject);
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 === 'NEW_ROW')) {
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 === 'NEW_ROW') {
203
+ if (!data.id && editing === NEW_ROW) {
190
204
  handleCreate(data.rowIndex);
191
205
  } else {
192
206
  handleSave(data.rowIndex);
193
207
  }
194
- toggleEditing(data.id);
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 === 'NEW_ROW') {
223
+ if (!data.id && editing === NEW_ROW) {
210
224
  fields.remove(data.rowIndex);
211
- toggleEditing('NEW_ROW');
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 React, { useEffect, useState, useContext } from 'react';
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
- refdataEndpoint: PropTypes.string
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
- refdataEndpoint
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
- setContentData(refdata?.sort(sortByDesc) ?? []);
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
- label: deleteModal?.refdata?.label,
106
- error: errorResp?.message
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 }),
@@ -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;