@k-int/stripes-kint-components 5.22.0 → 5.23.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
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
# [5.23.0](https://gitlab.com/knowledge-integration/folio/stripes-kint-components/compare/v5.22.0...v5.23.0) (2025-06-25)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* SearchKeyControl subIndexes ([40036a4](https://gitlab.com/knowledge-integration/folio/stripes-kint-components/commit/40036a4353f24e47480e8060d901ef44c5c3b6ed))
|
|
7
|
+
|
|
1
8
|
# [5.22.0](https://gitlab.com/knowledge-integration/folio/stripes-kint-components/compare/v5.21.0...v5.22.0) (2025-06-13)
|
|
2
9
|
|
|
3
10
|
|
|
@@ -25,9 +25,12 @@ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e
|
|
|
25
25
|
|
|
26
26
|
// Memoise this process so keyState changes if and only if options/qIndex change
|
|
27
27
|
const createKeyState = (0, _react.useCallback)(() => options.reduce((acc, curr) => {
|
|
28
|
+
// Is in use if URL contains ALL of the indexes
|
|
29
|
+
const subindexes = (curr?.indexes?.length ?? 0) > 0 ? curr.indexes : [curr.key];
|
|
28
30
|
acc[curr.key] = {
|
|
29
|
-
inUse: qIndexArray
|
|
30
|
-
label: curr.label ?? curr.key
|
|
31
|
+
inUse: subindexes.every(si => qIndexArray.includes(si)),
|
|
32
|
+
label: curr.label ?? curr.key,
|
|
33
|
+
subIndexes: curr.indexes
|
|
31
34
|
};
|
|
32
35
|
return acc;
|
|
33
36
|
}, {}), [options, qIndexArray]);
|
|
@@ -48,11 +51,44 @@ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e
|
|
|
48
51
|
} = (0, _hooks.usePrevNextPagination)({
|
|
49
52
|
defaultToPageOne: false
|
|
50
53
|
});
|
|
54
|
+
const handleSearchKeyChange = (0, _react.useCallback)(_ref2 => {
|
|
55
|
+
let {
|
|
56
|
+
e: {
|
|
57
|
+
target: {
|
|
58
|
+
checked: targetIsChecked
|
|
59
|
+
} = {}
|
|
60
|
+
} = {},
|
|
61
|
+
key,
|
|
62
|
+
value
|
|
63
|
+
} = _ref2;
|
|
64
|
+
// Set up which indexes need to change
|
|
65
|
+
const indicesToChange = (value?.subIndexes?.length ?? 0) > 0 ? value.subIndexes : [key];
|
|
66
|
+
|
|
67
|
+
// If false, we must remove from the qIndex
|
|
68
|
+
if (!targetIsChecked) {
|
|
69
|
+
indicesToChange.forEach(ind => {
|
|
70
|
+
const indexOfKey = qIndexArray.indexOf(ind);
|
|
71
|
+
if (indexOfKey > -1) {
|
|
72
|
+
// only splice array when item is found
|
|
73
|
+
qIndexArray.splice(indexOfKey, 1); // 2nd parameter means remove one item only
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
} else {
|
|
77
|
+
// If true, we need to add to qIndex
|
|
78
|
+
indicesToChange.forEach(ind => {
|
|
79
|
+
qIndexArray.push(ind);
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
setQIndex(qIndexArray?.join(','));
|
|
83
|
+
if (currentPage) {
|
|
84
|
+
resetPage();
|
|
85
|
+
}
|
|
86
|
+
}, [currentPage, qIndexArray, resetPage, setQIndex]);
|
|
51
87
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_jsxRuntime.Fragment, {
|
|
52
88
|
children: /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
|
|
53
89
|
className: _SearchKeyControl.default.searchKeyControlContainer,
|
|
54
|
-
children: Object.entries(keyState).map(
|
|
55
|
-
let [key, value] =
|
|
90
|
+
children: Object.entries(keyState).map(_ref3 => {
|
|
91
|
+
let [key, value] = _ref3;
|
|
56
92
|
/* At this point we have "key" corresponding to a searchKey option,
|
|
57
93
|
* and "value" an object of the shape
|
|
58
94
|
{
|
|
@@ -64,23 +100,11 @@ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e
|
|
|
64
100
|
checked: value?.inUse,
|
|
65
101
|
className: _SearchKeyControl.default.searchKeyControlElement,
|
|
66
102
|
label: value?.label,
|
|
67
|
-
onChange: e => {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
// only splice array when item is found
|
|
73
|
-
qIndexArray.splice(indexOfKey, 1); // 2nd parameter means remove one item only
|
|
74
|
-
}
|
|
75
|
-
} else {
|
|
76
|
-
// If true, we need to add to qIndex
|
|
77
|
-
qIndexArray.push(key);
|
|
78
|
-
}
|
|
79
|
-
setQIndex(qIndexArray?.join(','));
|
|
80
|
-
if (currentPage) {
|
|
81
|
-
resetPage();
|
|
82
|
-
}
|
|
83
|
-
}
|
|
103
|
+
onChange: e => handleSearchKeyChange({
|
|
104
|
+
e,
|
|
105
|
+
key,
|
|
106
|
+
value
|
|
107
|
+
})
|
|
84
108
|
}, `search-key-control-${key}`);
|
|
85
109
|
})
|
|
86
110
|
})
|
package/package.json
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
# SearchKeyControl
|
|
2
2
|
|
|
3
|
-
A component that renders a group of checkboxes allowing users to select one or more search keys (indexes). It
|
|
3
|
+
A component that renders a group of checkboxes allowing users to select one or more search keys (indexes). It synchronizes its state with the `qIndex` URL parameter (managed via the `useQIndex` hook), updating this parameter by injecting or removing individual keys based on user interactions.
|
|
4
4
|
|
|
5
|
-
> **Note:** This component is
|
|
5
|
+
> **Note:** This component is **controlled by the `qIndex` URL parameter** rather than external props like `value` or `onChange`. When users interact with checkboxes, the component modifies the `qIndex` by adding or removing search keys accordingly. It **does not** replace the entire `qIndex` value.
|
|
6
|
+
|
|
7
|
+
---
|
|
6
8
|
|
|
7
9
|
## Basic Usage
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
Provide an `options` array with each search key to be rendered. Each option includes a `key`, a display `label`, and optionally, an `indexes` array to represent multiple underlying sub-indexes.
|
|
10
12
|
|
|
11
13
|
```jsx
|
|
12
14
|
import { SearchKeyControl } from '@k-int/stripes-kint-components';
|
|
@@ -16,55 +18,78 @@ const MySearchForm = () => {
|
|
|
16
18
|
{ key: 'keyword', label: 'Keyword' },
|
|
17
19
|
{ key: 'title', label: 'Title' },
|
|
18
20
|
{ key: 'author', label: 'Author' },
|
|
19
|
-
{
|
|
21
|
+
{
|
|
22
|
+
key: 'subjectPlus',
|
|
23
|
+
label: 'Subject (incl. subdivisions)',
|
|
24
|
+
indexes: ['subject', 'subdivision']
|
|
25
|
+
}
|
|
20
26
|
];
|
|
21
27
|
|
|
22
28
|
return (
|
|
23
29
|
<div>
|
|
24
|
-
{/* ... other search form elements ... */}
|
|
25
30
|
<label>Search Indexes:</label>
|
|
26
31
|
<SearchKeyControl options={searchKeyOptions} />
|
|
27
|
-
{/* ... submit button etc ... */}
|
|
28
32
|
</div>
|
|
29
33
|
);
|
|
30
34
|
};
|
|
31
35
|
|
|
32
36
|
export default MySearchForm;
|
|
33
|
-
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
In this example:
|
|
34
40
|
|
|
35
|
-
|
|
41
|
+
* **Checkboxes** are rendered for each `key`, including the composite `subjectPlus`.
|
|
42
|
+
* The `qIndex` URL parameter (e.g. `?qIndex=title,author`) determines the checked state.
|
|
43
|
+
* Clicking a checkbox adds or removes the relevant index or indexes:
|
|
36
44
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
- It also automatically resets pagination (via `usePrevNextPagination`) when the `qIndex` changes.
|
|
45
|
+
* For simple entries like `"title"`, toggling directly modifies `qIndex`.
|
|
46
|
+
* For composite entries like `"subjectPlus"`, checking adds both `"subject"` and `"subdivision"`; unchecking removes both.
|
|
47
|
+
* **Pagination is reset** automatically via `usePrevNextPagination()` when the `qIndex` changes.
|
|
48
|
+
|
|
49
|
+
---
|
|
43
50
|
|
|
44
51
|
## Props
|
|
45
52
|
|
|
46
|
-
| Name | Type | Description
|
|
47
|
-
|
|
48
|
-
| `options` | `arrayOf(object)` |
|
|
53
|
+
| Name | Type | Description | Default | Required |
|
|
54
|
+
| :-------- | :---------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :------ | :------- |
|
|
55
|
+
| `options` | `arrayOf(object)` | Array of search key definitions. Each object must include:<br>• `key` (string): A unique identifier.<br>• `label` (string \| node): The displayed label.<br>• `indexes` (array of strings, optional): One or more sub-indexes that this checkbox controls (defaults to `[key]` if omitted). | `[]` | ✕ |
|
|
49
56
|
|
|
57
|
+
---
|
|
50
58
|
|
|
51
59
|
## How It Works
|
|
52
60
|
|
|
53
|
-
1.
|
|
54
|
-
|
|
61
|
+
### 1. Reading Search Index State
|
|
62
|
+
|
|
63
|
+
The component uses `useQIndex()` to retrieve and manage the `qIndex` URL parameter. The value is split into an array of active keys (`qIndexArray`), trimmed for whitespace and memoized.
|
|
64
|
+
|
|
65
|
+
### 2. Creating Internal State
|
|
66
|
+
|
|
67
|
+
A `keyState` object maps each option's `key` to:
|
|
68
|
+
|
|
69
|
+
* `inUse`: A boolean indicating whether all of the entry’s `indexes` are present in `qIndexArray`.
|
|
70
|
+
* `label`: The label to display next to the checkbox.
|
|
71
|
+
* `subIndexes`: The underlying `indexes` array (or `[key]` if not specified).
|
|
72
|
+
|
|
73
|
+
This mapping is memoized via `useCallback()` and kept in sync with URL state using `useEffect()`.
|
|
74
|
+
|
|
75
|
+
### 3. Rendering Checkboxes
|
|
76
|
+
|
|
77
|
+
Each `keyState` entry is rendered as a checkbox using `@folio/stripes/components`' `Checkbox`. Its `checked` state is tied to `inUse`, and its `label` comes from the `label` field.
|
|
78
|
+
|
|
79
|
+
### 4. Handling Changes
|
|
80
|
+
|
|
81
|
+
On user interaction:
|
|
82
|
+
|
|
83
|
+
* If a checkbox is checked:
|
|
84
|
+
|
|
85
|
+
* The component **adds** all `subIndexes` for the key to the `qIndexArray`.
|
|
86
|
+
* If a checkbox is unchecked:
|
|
55
87
|
|
|
56
|
-
|
|
57
|
-
It uses `useState`, `useEffect`, `useMemo`, and `useCallback` along with `lodash/isEqual` to create and maintain an internal `keyState` object. This object maps each `key` from the `options` prop to its display `label` and an `inUse` boolean flag. The `inUse` flag is determined by checking if the option's `key` exists within the current `qIndexArray` derived from the `qIndex` URL parameter. This ensures the checkboxes visually reflect the state in the URL.
|
|
88
|
+
* The component **removes** all `subIndexes` for the key from `qIndexArray`.
|
|
58
89
|
|
|
59
|
-
|
|
60
|
-
The component iterates over the `keyState` object and renders a `@folio/stripes/components` `Checkbox` for each entry. The `checked` prop of the checkbox is bound to the `inUse` flag from `keyState`, and the `label` is set accordingly.
|
|
90
|
+
The updated array is joined back into a comma-separated string and passed to `setQIndex()` to update the URL.
|
|
61
91
|
|
|
62
|
-
|
|
63
|
-
When a checkbox's state is changed by the user:
|
|
92
|
+
### 5. Pagination Reset
|
|
64
93
|
|
|
65
|
-
|
|
66
|
-
* It updates a temporary copy of the `qIndexArray` (either adding the key with `push` or removing it with `splice`).
|
|
67
|
-
* It calls `setQIndex` (from `useQIndex`) with the modified array joined back into a comma-separated string, which updates the URL parameter.
|
|
94
|
+
If the current page is non-zero (via `usePrevNextPagination`), the component calls `resetPage()` after modifying `qIndex`. This ensures consistency in result navigation after a filter change.
|
|
68
95
|
|
|
69
|
-
5. **Pagination Reset:**
|
|
70
|
-
The component also uses the `usePrevNextPagination` hook. If pagination is active (`currentPage` has a value), it calls `resetPage()` after updating the `qIndex`. This ensures that users are returned to the first page of results when the search indexes change, preventing potentially invalid page numbers for the new search scope.
|
|
@@ -23,7 +23,14 @@ const SearchKeyControl = ({
|
|
|
23
23
|
// Memoise this process so keyState changes if and only if options/qIndex change
|
|
24
24
|
const createKeyState = useCallback(() => (
|
|
25
25
|
options.reduce((acc, curr) => {
|
|
26
|
-
|
|
26
|
+
// Is in use if URL contains ALL of the indexes
|
|
27
|
+
const subindexes = ((curr?.indexes?.length ?? 0) > 0) ? curr.indexes : [curr.key];
|
|
28
|
+
|
|
29
|
+
acc[curr.key] = {
|
|
30
|
+
inUse: subindexes.every(si => qIndexArray.includes(si)),
|
|
31
|
+
label: curr.label ?? curr.key,
|
|
32
|
+
subIndexes: curr.indexes
|
|
33
|
+
};
|
|
27
34
|
return acc;
|
|
28
35
|
}, {})
|
|
29
36
|
), [options, qIndexArray]);
|
|
@@ -41,6 +48,31 @@ const SearchKeyControl = ({
|
|
|
41
48
|
// Check to see if page param exists, and if it does then changing qIndex should reset it
|
|
42
49
|
const { currentPage, resetPage } = usePrevNextPagination({ defaultToPageOne: false });
|
|
43
50
|
|
|
51
|
+
const handleSearchKeyChange = useCallback(({ e: { target: { checked: targetIsChecked } = {} } = {}, key, value }) => {
|
|
52
|
+
// Set up which indexes need to change
|
|
53
|
+
const indicesToChange = ((value?.subIndexes?.length ?? 0) > 0) ? value.subIndexes : [key];
|
|
54
|
+
|
|
55
|
+
// If false, we must remove from the qIndex
|
|
56
|
+
if (!targetIsChecked) {
|
|
57
|
+
indicesToChange.forEach(ind => {
|
|
58
|
+
const indexOfKey = qIndexArray.indexOf(ind);
|
|
59
|
+
if (indexOfKey > -1) { // only splice array when item is found
|
|
60
|
+
qIndexArray.splice(indexOfKey, 1); // 2nd parameter means remove one item only
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
} else {
|
|
64
|
+
// If true, we need to add to qIndex
|
|
65
|
+
indicesToChange.forEach(ind => {
|
|
66
|
+
qIndexArray.push(ind);
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
setQIndex(qIndexArray?.join(','));
|
|
71
|
+
if (currentPage) {
|
|
72
|
+
resetPage();
|
|
73
|
+
}
|
|
74
|
+
}, [currentPage, qIndexArray, resetPage, setQIndex]);
|
|
75
|
+
|
|
44
76
|
return (
|
|
45
77
|
<>
|
|
46
78
|
<div className={css.searchKeyControlContainer}>
|
|
@@ -59,23 +91,7 @@ const SearchKeyControl = ({
|
|
|
59
91
|
checked={value?.inUse}
|
|
60
92
|
className={css.searchKeyControlElement}
|
|
61
93
|
label={value?.label}
|
|
62
|
-
onChange={e => {
|
|
63
|
-
// If false, we must remove from the qIndex
|
|
64
|
-
if (!e.target.checked) {
|
|
65
|
-
const indexOfKey = qIndexArray.indexOf(key);
|
|
66
|
-
if (indexOfKey > -1) { // only splice array when item is found
|
|
67
|
-
qIndexArray.splice(indexOfKey, 1); // 2nd parameter means remove one item only
|
|
68
|
-
}
|
|
69
|
-
} else {
|
|
70
|
-
// If true, we need to add to qIndex
|
|
71
|
-
qIndexArray.push(key);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
setQIndex(qIndexArray?.join(','));
|
|
75
|
-
if (currentPage) {
|
|
76
|
-
resetPage();
|
|
77
|
-
}
|
|
78
|
-
}}
|
|
94
|
+
onChange={e => handleSearchKeyChange({ e, key, value })}
|
|
79
95
|
/>
|
|
80
96
|
);
|
|
81
97
|
})
|