@k-int/stripes-kint-components 5.17.0 → 5.19.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 +20 -0
- package/LICENSE +1 -1
- package/es/index.js +32 -12
- package/es/lib/EditableSettingsList/EditableSettingsList.js +4 -3
- package/es/lib/EditableSettingsList/EditableSettingsListFieldArray/EditableSettingsListFieldArray.js +110 -0
- package/es/lib/EditableSettingsList/{EditableSettingsListFieldArray.test.js → EditableSettingsListFieldArray/EditableSettingsListFieldArray.test.js} +3 -4
- package/es/lib/EditableSettingsList/EditableSettingsListFieldArray/index.js +13 -0
- package/es/lib/EditableSettingsList/SettingField/{EditSettingValue.js → EditSettingValue/EditSettingValue.js} +2 -2
- package/es/lib/EditableSettingsList/SettingField/{EditSettingValue.test.js → EditSettingValue/EditSettingValue.test.js} +2 -3
- package/es/lib/EditableSettingsList/SettingField/EditSettingValue/index.js +13 -0
- package/es/lib/EditableSettingsList/SettingField/{RenderSettingValue.js → RenderSettingValue/RenderSettingValue.js} +1 -1
- package/es/lib/EditableSettingsList/SettingField/{RenderSettingValue.test.js → RenderSettingValue/RenderSettingValue.test.js} +2 -2
- package/es/lib/EditableSettingsList/SettingField/RenderSettingValue/index.js +13 -0
- package/es/lib/EditableSettingsList/SettingField/SettingField.js +5 -5
- package/es/lib/EditableSettingsList/SettingField/SettingField.test.js +54 -44
- package/es/lib/EditableSettingsList/index.js +12 -0
- package/es/lib/SASQLookupComponent/SASQLookupComponent.js +5 -1
- package/es/lib/SearchKeyControl/SearchKeyControl.js +95 -0
- package/es/lib/SearchKeyControl/SearchKeyControl.test.js +177 -0
- package/es/lib/SearchKeyControl/index.js +13 -0
- package/es/lib/SettingPage/SettingPage.js +3 -1
- package/es/lib/SettingPage/{SettingPagePane.js → SettingPagePane/SettingPagePane.js} +2 -2
- package/es/lib/SettingPage/SettingPagePane/index.js +13 -0
- package/es/lib/hooks/__mocks__/index.js +2 -2
- package/es/lib/hooks/index.js +33 -21
- package/es/lib/hooks/intlHooks/index.js +27 -0
- package/es/lib/hooks/intlHooks/useIntlKey/index.js +13 -0
- package/es/lib/hooks/{useIntlKey.js → intlHooks/useIntlKey/useIntlKey.js} +1 -1
- package/es/lib/hooks/intlHooks/useIntlKeyStore/index.js +13 -0
- package/es/lib/hooks/intlHooks/useKintIntl/index.js +13 -0
- package/es/lib/hooks/{useKintIntl.js → intlHooks/useKintIntl/useKintIntl.js} +1 -1
- package/es/lib/hooks/useInvalidateRefdata/index.js +13 -0
- package/es/lib/hooks/{useInvalidateRefdata.js → useInvalidateRefdata/useInvalidateRefdata.js} +1 -1
- package/es/lib/hooks/useMutateCustomProperties/index.js +13 -0
- package/es/lib/hooks/{useMutateCustomProperties.js → useMutateCustomProperties/useMutateCustomProperties.js} +1 -1
- package/es/lib/hooks/useMutateGeneric/index.js +13 -0
- package/es/lib/hooks/useMutateRefdataCategory/index.js +13 -0
- package/es/lib/hooks/{useMutateRefdataCategory.js → useMutateRefdataCategory/useMutateRefdataCategory.js} +2 -2
- package/es/lib/hooks/useMutateRefdataValue/index.js +13 -0
- package/es/lib/hooks/{useMutateRefdataValue.js → useMutateRefdataValue/useMutateRefdataValue.js} +2 -2
- package/es/lib/settingsHooks/useAppSettings/index.js +13 -0
- package/es/lib/settingsHooks/{useAppSettings.js → useAppSettings/useAppSettings.js} +1 -1
- package/es/lib/settingsHooks/useSettingSection/index.js +13 -0
- package/es/lib/settingsHooks/{useSettingSection.js → useSettingSection/useSettingSection.js} +1 -1
- package/es/lib/settingsHooks/useSettings/index.js +13 -0
- package/es/lib/settingsHooks/{useSettings.js → useSettings/useSettings.js} +10 -9
- package/es/lib/utils/refdataQueryKey/index.js +13 -0
- package/package.json +1 -1
- package/src/index.js +8 -3
- package/src/lib/ActionList/README.md +18 -18
- package/src/lib/EditableSettingsList/EditableSettingsList.js +3 -2
- package/src/lib/EditableSettingsList/EditableSettingsListFieldArray/EditableSettingsListFieldArray.js +118 -0
- package/src/lib/EditableSettingsList/{EditableSettingsListFieldArray.test.js → EditableSettingsListFieldArray/EditableSettingsListFieldArray.test.js} +3 -5
- package/src/lib/EditableSettingsList/EditableSettingsListFieldArray/README.md +68 -0
- package/src/lib/EditableSettingsList/EditableSettingsListFieldArray/index.js +1 -0
- package/src/lib/EditableSettingsList/README.md +74 -0
- package/src/lib/EditableSettingsList/SettingField/{EditSettingValue.js → EditSettingValue/EditSettingValue.js} +2 -2
- package/src/lib/EditableSettingsList/SettingField/{EditSettingValue.test.js → EditSettingValue/EditSettingValue.test.js} +2 -5
- package/src/lib/EditableSettingsList/SettingField/EditSettingValue/README.md +63 -0
- package/src/lib/EditableSettingsList/SettingField/EditSettingValue/index.js +1 -0
- package/src/lib/EditableSettingsList/SettingField/README.md +61 -0
- package/src/lib/EditableSettingsList/SettingField/{RenderSettingValue.js → RenderSettingValue/RenderSettingValue.js} +1 -1
- package/src/lib/EditableSettingsList/SettingField/{RenderSettingValue.test.js → RenderSettingValue/RenderSettingValue.test.js} +2 -2
- package/src/lib/EditableSettingsList/SettingField/RenderSettingValue/index.js +1 -0
- package/src/lib/EditableSettingsList/SettingField/SettingField.js +5 -3
- package/src/lib/EditableSettingsList/SettingField/SettingField.test.js +65 -44
- package/src/lib/EditableSettingsList/index.js +1 -1
- package/src/lib/NumberField/README.md +134 -0
- package/src/lib/SASQLookupComponent/README.md +172 -0
- package/src/lib/SASQLookupComponent/SASQLookupComponent.js +6 -1
- package/src/lib/SASQLookupComponent/TableBody/README.md +113 -0
- package/src/lib/SASQRoute/README.md +49 -18
- package/src/lib/SASQViewComponent/README.md +132 -0
- package/src/lib/SearchKeyControl/README.md +70 -0
- package/src/lib/SearchKeyControl/SearchKeyControl.js +98 -0
- package/src/lib/SearchKeyControl/SearchKeyControl.test.js +165 -0
- package/src/lib/SearchKeyControl/index.js +1 -0
- package/src/lib/SettingPage/README.md +66 -0
- package/src/lib/SettingPage/SettingPage.js +3 -1
- package/src/lib/SettingPage/SettingPagePane/README.md +31 -0
- package/src/lib/SettingPage/{SettingPagePane.js → SettingPagePane/SettingPagePane.js} +2 -2
- package/src/lib/SettingPage/SettingPagePane/index.js +1 -0
- package/src/lib/hooks/README.md +26 -121
- package/src/lib/hooks/__mocks__/index.js +2 -2
- package/src/lib/hooks/index.js +2 -3
- package/src/lib/hooks/intlHooks/README.md +31 -0
- package/src/lib/hooks/intlHooks/index.js +3 -0
- package/src/lib/hooks/intlHooks/useIntlKey/README.md +23 -0
- package/src/lib/hooks/intlHooks/useIntlKey/index.js +1 -0
- package/src/lib/hooks/{useIntlKey.js → intlHooks/useIntlKey/useIntlKey.js} +1 -1
- package/src/lib/hooks/intlHooks/useIntlKeyStore/README.md +32 -0
- package/src/lib/hooks/intlHooks/useIntlKeyStore/index.js +1 -0
- package/src/lib/hooks/intlHooks/useKintIntl/README.md +42 -0
- package/src/lib/hooks/intlHooks/useKintIntl/index.js +1 -0
- package/src/lib/hooks/{useKintIntl.js → intlHooks/useKintIntl/useKintIntl.js} +1 -1
- package/src/lib/hooks/useInvalidateRefdata/README.md +72 -0
- package/src/lib/hooks/useInvalidateRefdata/index.js +1 -0
- package/src/lib/hooks/{useInvalidateRefdata.js → useInvalidateRefdata/useInvalidateRefdata.js} +1 -1
- package/src/lib/hooks/useMutateCustomProperties/README.md +88 -0
- package/src/lib/hooks/useMutateCustomProperties/index.js +1 -0
- package/src/lib/hooks/{useMutateCustomProperties.js → useMutateCustomProperties/useMutateCustomProperties.js} +1 -1
- package/src/lib/hooks/useMutateGeneric/README.md +187 -0
- package/src/lib/hooks/useMutateGeneric/index.js +1 -0
- package/src/lib/hooks/useMutateRefdataCategory/README.md +85 -0
- package/src/lib/hooks/useMutateRefdataCategory/index.js +1 -0
- package/src/lib/hooks/{useMutateRefdataCategory.js → useMutateRefdataCategory/useMutateRefdataCategory.js} +2 -2
- package/src/lib/hooks/useMutateRefdataValue/README.md +154 -0
- package/src/lib/hooks/useMutateRefdataValue/index.js +1 -0
- package/src/lib/hooks/{useMutateRefdataValue.js → useMutateRefdataValue/useMutateRefdataValue.js} +2 -2
- package/src/lib/settingsHooks/useAppSettings/README.md +24 -0
- package/src/lib/settingsHooks/useAppSettings/index.js +1 -0
- package/src/lib/settingsHooks/{useAppSettings.js → useAppSettings/useAppSettings.js} +1 -1
- package/src/lib/settingsHooks/useSettingSection/README.md +54 -0
- package/src/lib/settingsHooks/useSettingSection/index.js +1 -0
- package/src/lib/settingsHooks/{useSettingSection.js → useSettingSection/useSettingSection.js} +1 -1
- package/src/lib/settingsHooks/useSettings/README.md +84 -0
- package/src/lib/settingsHooks/useSettings/index.js +1 -0
- package/src/lib/settingsHooks/{useSettings.js → useSettings/useSettings.js} +10 -7
- package/src/lib/utils/refdataQueryKey/README.md +38 -0
- package/src/lib/utils/refdataQueryKey/index.js +1 -0
- package/styles/SearchKeyControl.css +14 -0
- package/es/lib/EditableSettingsList/EditableSettingsListFieldArray.js +0 -57
- package/src/lib/EditableSettingsList/EditableSettingsListFieldArray.js +0 -58
- /package/es/lib/hooks/{useIntlKeyStore.js → intlHooks/useIntlKeyStore/useIntlKeyStore.js} +0 -0
- /package/es/lib/hooks/{useMutateGeneric.js → useMutateGeneric/useMutateGeneric.js} +0 -0
- /package/es/lib/utils/{refdataQueryKey.js → refdataQueryKey/refdataQueryKey.js} +0 -0
- /package/src/lib/hooks/{useIntlKeyStore.js → intlHooks/useIntlKeyStore/useIntlKeyStore.js} +0 -0
- /package/src/lib/hooks/{useMutateGeneric.js → useMutateGeneric/useMutateGeneric.js} +0 -0
- /package/src/lib/utils/{refdataQueryKey.js → refdataQueryKey/refdataQueryKey.js} +0 -0
|
@@ -28,6 +28,7 @@ import { generateKiwtQuery } from '../utils';
|
|
|
28
28
|
import { useKintIntl, useKiwtSASQuery, useLocalStorageState, usePrevNextPagination } from '../hooks';
|
|
29
29
|
|
|
30
30
|
import TableBody from './TableBody';
|
|
31
|
+
import SearchKeyControl from '../SearchKeyControl';
|
|
31
32
|
|
|
32
33
|
const SASQLookupComponent = forwardRef((props, ref) => {
|
|
33
34
|
const {
|
|
@@ -59,8 +60,9 @@ const SASQLookupComponent = forwardRef((props, ref) => {
|
|
|
59
60
|
RenderBody,
|
|
60
61
|
rowNavigation = true, // Default navigation onRowClick
|
|
61
62
|
sasqProps,
|
|
63
|
+
searchableIndexes,
|
|
62
64
|
searchFieldAriaLabel,
|
|
63
|
-
searchFieldProps,
|
|
65
|
+
searchFieldProps = {},
|
|
64
66
|
} = props;
|
|
65
67
|
const kintIntl = useKintIntl(passedIntlKey, passedIntlNS);
|
|
66
68
|
const [count, setCount] = useState(0);
|
|
@@ -236,6 +238,9 @@ const SASQLookupComponent = forwardRef((props, ref) => {
|
|
|
236
238
|
value={searchValue.query}
|
|
237
239
|
{...searchFieldProps}
|
|
238
240
|
/>
|
|
241
|
+
{searchableIndexes.length > 0 && (
|
|
242
|
+
<SearchKeyControl options={searchableIndexes} />
|
|
243
|
+
)}
|
|
239
244
|
<Button
|
|
240
245
|
buttonStyle="primary"
|
|
241
246
|
disabled={!searchValue.query}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# SASQTableBody
|
|
2
|
+
|
|
3
|
+
A component that renders a Multi-Column List (MCL) for displaying tabular data with integrated sorting, row navigation, and infinite scrolling. **SASQTableBody** is designed to work within lookup workflows (like **SASQLookupComponent**) but can be used independently. It handles rendering of data, empty states, and integrates with pagination via a `fetchNextPage` callback.
|
|
4
|
+
|
|
5
|
+
## Basic Usage
|
|
6
|
+
|
|
7
|
+
Below is an example demonstrating how to use **SASQTableBody** with required props. Additional props are commented for context:
|
|
8
|
+
|
|
9
|
+
```jsx
|
|
10
|
+
import { useState } from 'react';
|
|
11
|
+
import { SASQTableBody } from '@k-int/stripes-kint-components';
|
|
12
|
+
|
|
13
|
+
const MyTableView = () => {
|
|
14
|
+
const [data] = useState({
|
|
15
|
+
totalRecords: 2,
|
|
16
|
+
results: [
|
|
17
|
+
{ id: '1', name: 'Item One', status: 'Active' },
|
|
18
|
+
{ id: '2', name: 'Item Two', status: 'Inactive' }
|
|
19
|
+
]
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
const query = { sort: 'name' }; // Used for sorting and empty state messages
|
|
23
|
+
const resultColumns = [
|
|
24
|
+
{ propertyPath: 'name', label: 'Name' },
|
|
25
|
+
{ propertyPath: 'status', label: 'Status' }
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
const fetchNextPage = ({ pageParam }) => {
|
|
29
|
+
console.log('Fetching page:', pageParam);
|
|
30
|
+
// Implement pagination logic here
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<SASQTableBody
|
|
35
|
+
data={data}
|
|
36
|
+
query={query}
|
|
37
|
+
path="/my-items" // Base path for row navigation
|
|
38
|
+
resultColumns={resultColumns}
|
|
39
|
+
fetchNextPage={fetchNextPage}
|
|
40
|
+
// mclProps={{ formatter: { name: (item) => <strong>{item.name}</strong> } }}
|
|
41
|
+
// rowNavigation={false}
|
|
42
|
+
/>
|
|
43
|
+
);
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export default MyTableView;
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Explanation
|
|
50
|
+
|
|
51
|
+
- **data:**
|
|
52
|
+
Contains `results` (array of items) and `totalRecords` (total items for pagination).
|
|
53
|
+
- **query:**
|
|
54
|
+
Provides current search/sort state. Must include `sort` and `query` if applicable.
|
|
55
|
+
- **path:**
|
|
56
|
+
Base URL path for constructing row navigation links (e.g., `/my-items/1`).
|
|
57
|
+
- **resultColumns:**
|
|
58
|
+
Defines table columns via `propertyPath` (data key) and `label` (header text).
|
|
59
|
+
- **fetchNextPage:**
|
|
60
|
+
Called when more data is needed (e.g., scrolling to the bottom).
|
|
61
|
+
|
|
62
|
+
## Props
|
|
63
|
+
|
|
64
|
+
| Prop | Type | Required | Description |
|
|
65
|
+
|---------------------|--------------------------------------------------|----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
66
|
+
| `data` | `{ totalRecords: number, results: object[] }` | ✓ | Data to display. `results` contains the items, `totalRecords` sets pagination bounds. |
|
|
67
|
+
| `query` | `object` | ✓ | Current query state (e.g., `{ sort: 'name', query: 'search term' }`). Used for sorting and empty state messages. |
|
|
68
|
+
| `path` | `string` | ✓ | Base path for row URLs. Each row links to `{path}/{rowData.id}`. |
|
|
69
|
+
| `resultColumns` | `Array<{ propertyPath: string, label: string }>` | ✓ | Columns to render. `propertyPath` accesses the data field, `label` sets the header. |
|
|
70
|
+
| `fetchNextPage` | `({ pageParam: number }) => void` | ✓ | Callback to load more data. `pageParam` indicates the next page index. |
|
|
71
|
+
| `mclProps` | `object` | ✕ | Props passed to the underlying [MultiColumnList](https://github.com/folio-org/stripes-components/tree/master/lib/MultiColumnList). Example: `formatter`, `id`. |
|
|
72
|
+
| `rowNavigation` | `boolean` | ✕ | Enables navigation to detail views on row click. Default: `true`. |
|
|
73
|
+
| `filterPaneVisible` | `boolean` | ✕ | Toggles filter pane visibility in `NoResultsMessage`. |
|
|
74
|
+
| `toggleFilterPane` | `() => void` | ✕ | Callback to toggle filter pane. Used in `NoResultsMessage`. |
|
|
75
|
+
| `intlKey` | `string` | ✕ | Base key for internationalizing labels. See `useKintIntl`. |
|
|
76
|
+
| `intlNS` | `string` | ✕ | Namespace for internationalization. See `useKintIntl`. |
|
|
77
|
+
| `labelOverrides` | `object` | ✕ | Overrides default labels (e.g., `{ noResultsFound: 'No items' }`). |
|
|
78
|
+
| `match` | `object` | ✕ | React Router `match` object. Used to highlight the selected row via URL `id` param. |
|
|
79
|
+
| `isLoading` | `boolean` | ✕ | Shows loading state in `NoResultsMessage`. |
|
|
80
|
+
| `isError` | `boolean` | ✕ | Shows error state in `NoResultsMessage`. |
|
|
81
|
+
| `error` | `object` | ✕ | Error object displayed in `NoResultsMessage`. |
|
|
82
|
+
|
|
83
|
+
## Key Features
|
|
84
|
+
|
|
85
|
+
1. **Row Navigation:**
|
|
86
|
+
When `rowNavigation={true}` (default), clicking a row navigates to `{path}/{rowData.id}`. Uses `react-router` internally.
|
|
87
|
+
|
|
88
|
+
2. **Sorting:**
|
|
89
|
+
Column headers trigger `onSort`, updating the `query.sort` value. The current sort is derived from `query.sort`.
|
|
90
|
+
|
|
91
|
+
3. **Infinite Scroll:**
|
|
92
|
+
`fetchNextPage` is called automatically as the user scrolls. Can alternatively use with pagination logic (e.g., `usePrevNextPagination` props passed to mclProps).
|
|
93
|
+
|
|
94
|
+
4. **Custom Formatters:**
|
|
95
|
+
Pass a `formatter` in `mclProps` to customize cell rendering. Each formatter receives the row data and a `defaultRowUrl`:
|
|
96
|
+
```jsx
|
|
97
|
+
mclProps={{
|
|
98
|
+
formatter: {
|
|
99
|
+
name: (item) => <Link to={item.defaultRowUrl}>{item.name}</Link>
|
|
100
|
+
}
|
|
101
|
+
}}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
5. **Empty States:**
|
|
105
|
+
`NoResultsMessage` handles loading, errors, and no-results states.
|
|
106
|
+
|
|
107
|
+
## Integration Notes
|
|
108
|
+
|
|
109
|
+
- **Parent Components:**
|
|
110
|
+
Designed to work within **SASQLookupComponent** (as its default `RenderBody`), but can be used standalone.
|
|
111
|
+
|
|
112
|
+
- **Routing:**
|
|
113
|
+
Must be rendered within a `react-router` context for navigation to work.
|
|
@@ -1,33 +1,38 @@
|
|
|
1
1
|
# SASQRoute
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
A component designed to accelerate the setup of a basic 3-pane layout by combining routing, SASQ (Search And Sort Query), and Multi-Column List (MCL) configuration in one place. **SASQRoute** simplifies the process of configuring lookup and view components by automatically setting up the required routes and passing down fetch parameters and custom components.
|
|
4
|
+
|
|
5
|
+
> **Note:** Although **SASQRoute** is intended to work with the default sub-components—**SASQLookupComponent** and **SASQViewComponent**—you can override them via props. Refer to the documentation for those sub-components for further details.
|
|
3
6
|
|
|
4
7
|
## Basic Usage
|
|
5
|
-
|
|
6
|
-
|
|
8
|
+
|
|
9
|
+
Below is an example of how to set up **SASQRoute**. In this example, it configures a route that manages both a lookup view and an individual item view within a 3-pane layout. The lookup component renders an MCL with the given `resultColumns`, and clicking on a row navigates to the view route that renders the specified `ViewComponent`.
|
|
10
|
+
|
|
11
|
+
```jsx
|
|
7
12
|
import { FormattedMessage } from 'react-intl';
|
|
8
13
|
|
|
9
14
|
import { SASQRoute } from '@k-int/stripes-kint-components';
|
|
10
15
|
import ActionItem from '../components/ActionItem';
|
|
11
16
|
|
|
12
17
|
const ActionedRoute = ({ path }) => {
|
|
13
|
-
|
|
14
18
|
const fetchParameters = {
|
|
15
19
|
endpoint: "remote-sync/feedback/done",
|
|
16
20
|
itemEndpoint: "remote-sync/feedback",
|
|
17
21
|
SASQ_MAP: {
|
|
18
22
|
searchKey: 'description',
|
|
19
23
|
filterKeys: {
|
|
24
|
+
// Additional filter configuration if required
|
|
20
25
|
}
|
|
21
26
|
}
|
|
22
27
|
};
|
|
23
28
|
|
|
24
29
|
const resultColumns = [
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
];
|
|
30
|
+
{ propertyPath: "selected", label: " " },
|
|
31
|
+
{ propertyPath: "description", label: <FormattedMessage id="ui-remote-sync.prop.feedback.description" /> },
|
|
32
|
+
{ propertyPath: "status", label: <FormattedMessage id="ui-remote-sync.prop.feedback.status" /> },
|
|
33
|
+
{ propertyPath: "correlationId", label: <FormattedMessage id="ui-remote-sync.prop.feedback.correlationId" /> },
|
|
34
|
+
{ propertyPath: "caseIndicator", label: <FormattedMessage id="ui-remote-sync.prop.feedback.caseIndicator" /> }
|
|
35
|
+
];
|
|
31
36
|
|
|
32
37
|
return (
|
|
33
38
|
<SASQRoute
|
|
@@ -41,15 +46,41 @@ const ActionedRoute = ({ path }) => {
|
|
|
41
46
|
};
|
|
42
47
|
```
|
|
43
48
|
|
|
44
|
-
|
|
49
|
+
In this example, **SASQRoute**:
|
|
50
|
+
- Configures lookup behavior via `fetchParameters` and the associated `SASQ_MAP`.
|
|
51
|
+
- Uses `resultColumns` to define the columns for the MCL rendered by the lookup component.
|
|
52
|
+
- Sets up routes under the provided `path` for both the lookup and view panes.
|
|
53
|
+
- Renders the `ActionItem` component when an individual row is selected.
|
|
54
|
+
|
|
45
55
|
## Props
|
|
46
56
|
|
|
47
|
-
Name
|
|
48
|
-
|
|
49
|
-
fetchParameters
|
|
50
|
-
id
|
|
51
|
-
path
|
|
52
|
-
resultColumns
|
|
53
|
-
ViewComponent
|
|
57
|
+
| Name | Type | Description | Default | Required |
|
|
58
|
+
|-----------------------|------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------|----------|
|
|
59
|
+
| `fetchParameters` | `object` | An object containing the parameters needed for data fetching for both the lookup and view components. This should include:<br><br>• **endpoint:** The main fetch endpoint for retrieving all data. The endpoint is typically used for both list and individual item fetches (appending `/{id}` to the endpoint for individual items).<br><br>• **itemEndpoint (optional):** An alternative endpoint for fetching a single item.<br><br>• **SASQ_MAP:** An object defining search and filter configuration. See `generateKiwtQuery` for details. | | ✓ |
|
|
60
|
+
| `id` | `string` | A unique identifier for this route. This identifier is important as it drives pane set logic and must be unique within the application. | | ✓ |
|
|
61
|
+
| `path` | `string` | The base path for this route. **SASQRoute** will set up a 3-pane layout under this path and automatically configure the view route under `{path}/:id`. | | ✓ |
|
|
62
|
+
| `resultColumns` | `array` | An array of objects where each object defines a column for the Multi-Column List (MCL) rendered by the lookup component. Each object should contain a `propertyPath` and a `label`. These columns drive the display of data in the list view. | | ✓ |
|
|
63
|
+
| `ViewComponent` | `element` (or `func`) | The component to render when a row in the lookup list is selected. This component is displayed in the view pane for showing detailed information about a selected item. | | ✓ |
|
|
64
|
+
| `children` | `node`, `element`, or `func` | Optional additional child elements to render within the lookup component. These are rendered inside the **SASQLookupComponent**. | | ✕ |
|
|
65
|
+
| `getPathLookup` | `func` | A function to determine the route for the lookup pane. Defaults to appending `/:id?` to the base `path`. Use this prop to customize the lookup route if needed. | `(path) => ${path}/:id?` | ✕ |
|
|
66
|
+
| `getPathView` | `func` | A function to determine the route for the view pane. Defaults to appending `/:id` to the base `path`. Use this prop to customize the view route if needed. | `(path) => ${path}/:id` | ✕ |
|
|
67
|
+
| `SASQLookupComponent` | `element` or `func` | The component to be used for the lookup view. Defaults to the built-in **SASQLookupComponent**. Override this prop to provide a custom lookup component. | `SASQLookupComponent` | ✕ |
|
|
68
|
+
| `SASQViewComponent` | `element` or `func` | The component to be used for the view pane. Defaults to the built-in **SASQViewComponent**. Override this prop to provide a custom view component. | `SASQViewComponent` | ✕ |
|
|
69
|
+
| `...props` | `object` | Additional props will be passed down to both the lookup and view components. Use these to further customize behavior or appearance as needed. | | ✕ |
|
|
70
|
+
|
|
71
|
+
## How It Works
|
|
72
|
+
|
|
73
|
+
1. **Configuration of SASQ_MAP:**
|
|
74
|
+
The component extracts and updates the `SASQ_MAP` from `fetchParameters`. If `perPage` is not defined, it defaults to 25. Additionally, it ensures that `stats` is set to `true` before reassigning the object back to `fetchParameters`.
|
|
75
|
+
|
|
76
|
+
2. **Routing Setup:**
|
|
77
|
+
- The **lookup route** is configured using the `getPathLookup` function (defaulting to appending `/:id?` to the base `path`). The lookup component (default **SASQLookupComponent**) renders the main Multi-Column List.
|
|
78
|
+
- Nested within the lookup route is a **view route** configured using the `getPathView` function (defaulting to appending `/:id`). This route renders the view component (default **SASQViewComponent**), which in turn displays the detailed view of a selected item via the passed `ViewComponent`.
|
|
79
|
+
|
|
80
|
+
3. **Forwarding Refs:**
|
|
81
|
+
The component uses `forwardRef` and `useImperativeHandle` to combine the refs of both the lookup and view components, allowing parent components to access methods or properties exposed by these sub-components.
|
|
54
82
|
|
|
83
|
+
4. **Customizability:**
|
|
84
|
+
By overriding default sub-components (`SASQLookupComponent` and `SASQViewComponent`), and by providing custom functions for route paths (`getPathLookup` and `getPathView`), developers can tailor the routing and display behavior to match specific application needs.
|
|
55
85
|
|
|
86
|
+
This comprehensive setup provided by **SASQRoute** allows for a rapid configuration of complex layouts involving data lookup and detailed views, reducing boilerplate and ensuring consistency across the application.
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# SASQViewComponent
|
|
2
|
+
|
|
3
|
+
A component that fetches and displays detailed data for a single item within a 3-pane layout. **SASQViewComponent** is typically used as the view pane in conjunction with **SASQLookupComponent** (for list views) via **SASQRoute**. It handles data fetching for individual records and renders content through a customizable `ViewComponent`.
|
|
4
|
+
|
|
5
|
+
## Basic Usage
|
|
6
|
+
|
|
7
|
+
**SASQViewComponent** is generally used indirectly through **SASQRoute**. Below is a minimal example showing direct usage (though typically configured via **SASQRoute**):
|
|
8
|
+
|
|
9
|
+
```jsx
|
|
10
|
+
import { SASQViewComponent } from '@k-int/stripes-kint-components';
|
|
11
|
+
import ItemDetailView from './ItemDetailView';
|
|
12
|
+
|
|
13
|
+
// In a route configuration (e.g., within a <Route> component)
|
|
14
|
+
const ViewPane = (routeProps) => (
|
|
15
|
+
<SASQViewComponent
|
|
16
|
+
fetchParameters={{
|
|
17
|
+
endpoint: '/api/items', // Default endpoint for individual items
|
|
18
|
+
itemEndpoint: '/api/items/details' // Optional: override for item-specific endpoint
|
|
19
|
+
}}
|
|
20
|
+
ViewComponent={ItemDetailView}
|
|
21
|
+
{...routeProps}
|
|
22
|
+
/>
|
|
23
|
+
);
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
In this example, when navigating to a URL like `/items/123`, **SASQViewComponent**:
|
|
27
|
+
1. Fetches data from `/api/items/details/123` (using `itemEndpoint`)
|
|
28
|
+
2. Renders `ItemDetailView` with the fetched data
|
|
29
|
+
3. Provides a default close button to return to the list view
|
|
30
|
+
|
|
31
|
+
## Props
|
|
32
|
+
|
|
33
|
+
| Prop | Type | Required | Description |
|
|
34
|
+
|-----------------------------|--------------------------------------------------|----------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
35
|
+
| `fetchParameters` | `object` | ✓ | Configuration for data fetching. Requires either:<br>• `endpoint`: Base endpoint (appended with `/{id}`)<br>• `itemEndpoint`: Specific endpoint for items (override) |
|
|
36
|
+
| `ViewComponent` | `Component` | ✓ | Component to render the item details. Receives `resource` (data), `onClose` (close handler), and `queryProps` (loading/error states). |
|
|
37
|
+
| `viewQueryNamespaceGenerator` | `function` | ✕ | Generates a unique query key for react-query. Default: Creates a namespace based on component ID and route params. |
|
|
38
|
+
| `id` | `string` | ✕ | Unique identifier for the component. Used in query key generation. |
|
|
39
|
+
| `path` | `string` | ✓ | Base path for navigation (e.g., `/items`). Used with `history` to return to list view. |
|
|
40
|
+
| `history` | `object` | ✓ | React Router history object (injected by parent route). |
|
|
41
|
+
| `location` | `object` | ✓ | React Router location object (injected by parent route). |
|
|
42
|
+
| `match` | `object` | ✓ | React Router match object containing route params (e.g., `match.params.id`). |
|
|
43
|
+
|
|
44
|
+
## Key Features
|
|
45
|
+
|
|
46
|
+
1. **Automatic Data Fetching**
|
|
47
|
+
Fetches individual item data when `match.params.id` exists. Uses either:
|
|
48
|
+
```js
|
|
49
|
+
`${itemEndpoint}/${id}` // If itemEndpoint provided
|
|
50
|
+
`${endpoint}/${id}` // If only endpoint provided
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
2. **Query Management**
|
|
54
|
+
Uses `react-query` for fetching, caching, and error handling. Exposes query states (`isLoading`, `isError`, etc.) via `queryProps` passed to `ViewComponent`.
|
|
55
|
+
|
|
56
|
+
3. **Close Behavior**
|
|
57
|
+
Provides a default `onClose` handler that navigates back to the parent path (e.g., from `/items/123` to `/items`).
|
|
58
|
+
|
|
59
|
+
4. **Customizable Query Keys**
|
|
60
|
+
Override `viewQueryNamespaceGenerator` to control react-query's cache key structure:
|
|
61
|
+
```jsx
|
|
62
|
+
<SASQViewComponent
|
|
63
|
+
viewQueryNamespaceGenerator={({ namespace, id }) => [
|
|
64
|
+
namespace,
|
|
65
|
+
'custom-view',
|
|
66
|
+
id
|
|
67
|
+
]}
|
|
68
|
+
/>
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
5. **Ref Forwarding**
|
|
72
|
+
Exposes query data and status via ref:
|
|
73
|
+
```jsx
|
|
74
|
+
const viewRef = useRef();
|
|
75
|
+
// Access { data, isLoading, ... } via viewRef.current.queryProps
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Integration with ViewComponent
|
|
79
|
+
|
|
80
|
+
Your `ViewComponent` receives these props:
|
|
81
|
+
|
|
82
|
+
| Prop | Type | Description |
|
|
83
|
+
|--------------|-----------|-------------------------------------------------------------------------------------------------|
|
|
84
|
+
| `resource` | `object` | Fetched data for the item. |
|
|
85
|
+
| `onClose` | `function`| Callback to close the view pane (returns to list). |
|
|
86
|
+
| `queryProps` | `object` | Contains `isLoading`, `isError`, `error`, and other react-query states. |
|
|
87
|
+
|
|
88
|
+
Example `ViewComponent` implementation:
|
|
89
|
+
```jsx
|
|
90
|
+
const ItemDetailView = ({ resource, onClose, queryProps }) => {
|
|
91
|
+
if (queryProps.isLoading) return <Spinner />;
|
|
92
|
+
if (queryProps.isError) return <Error message={queryProps.error.message} />;
|
|
93
|
+
|
|
94
|
+
return (
|
|
95
|
+
<div>
|
|
96
|
+
<button onClick={onClose}>Back</button>
|
|
97
|
+
<h1>{resource.name}</h1>
|
|
98
|
+
{/* Render other details */}
|
|
99
|
+
</div>
|
|
100
|
+
);
|
|
101
|
+
};
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Usage with SASQRoute
|
|
105
|
+
|
|
106
|
+
Typically used via **SASQRoute** for automatic route configuration:
|
|
107
|
+
```jsx
|
|
108
|
+
<SASQRoute
|
|
109
|
+
path="/items"
|
|
110
|
+
fetchParameters={{
|
|
111
|
+
endpoint: '/api/items',
|
|
112
|
+
SASQ_MAP: { searchKey: 'name' }
|
|
113
|
+
}}
|
|
114
|
+
ViewComponent={ItemDetailView}
|
|
115
|
+
resultColumns={[...]}
|
|
116
|
+
/>
|
|
117
|
+
```
|
|
118
|
+
This automatically:
|
|
119
|
+
- Creates routes for `/items` (list) and `/items/:id` (detail)
|
|
120
|
+
- Connects the lookup and view components
|
|
121
|
+
- Passes fetched data to `ItemDetailView`
|
|
122
|
+
|
|
123
|
+
## Edge Cases
|
|
124
|
+
|
|
125
|
+
- **Missing ID Parameter:**
|
|
126
|
+
If `match.params.id` is undefined, no data fetching occurs and `resource` will be `undefined`.
|
|
127
|
+
|
|
128
|
+
- **Custom Endpoints:**
|
|
129
|
+
Use `itemEndpoint` when individual items require a different API path than `{endpoint}/{id}`.
|
|
130
|
+
|
|
131
|
+
- **Query Key Collisions:**
|
|
132
|
+
Override `viewQueryNamespaceGenerator` if multiple SASQViewComponents share the same ID/route.
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# SearchKeyControl
|
|
2
|
+
|
|
3
|
+
A component that renders a group of checkboxes allowing users to select one or more search keys (indexes). It directly interacts with the `qIndex` URL parameter (managed via the `useQIndex` hook) to reflect the currently active search indexes.
|
|
4
|
+
|
|
5
|
+
> **Note:** This component is designed to be controlled by the `qIndex` URL parameter rather than receiving explicit `value` or `onChange` props for the entire set of keys. It modifies the `qIndex` parameter by adding or removing individual keys based on checkbox interactions.
|
|
6
|
+
|
|
7
|
+
## Basic Usage
|
|
8
|
+
|
|
9
|
+
Below is an example demonstrating how to use **SearchKeyControl**. You provide an array of `options`, each defining a potential search key with a label. The component automatically reads the current `qIndex` from the URL, determines the checked state of each checkbox, and updates the `qIndex` when checkboxes are toggled.
|
|
10
|
+
|
|
11
|
+
```jsx
|
|
12
|
+
import { SearchKeyControl } from '@k-int/stripes-kint-components';
|
|
13
|
+
|
|
14
|
+
const MySearchForm = () => {
|
|
15
|
+
const searchKeyOptions = [
|
|
16
|
+
{ key: 'keyword', label: 'Keyword' },
|
|
17
|
+
{ key: 'title', label: 'Title' },
|
|
18
|
+
{ key: 'author', label: 'Author' },
|
|
19
|
+
{ key: 'subject', label: 'Subject' }
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<div>
|
|
24
|
+
{/* ... other search form elements ... */}
|
|
25
|
+
<label>Search Indexes:</label>
|
|
26
|
+
<SearchKeyControl options={searchKeyOptions} />
|
|
27
|
+
{/* ... submit button etc ... */}
|
|
28
|
+
</div>
|
|
29
|
+
);
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export default MySearchForm;
|
|
33
|
+
````
|
|
34
|
+
|
|
35
|
+
In this example, **SearchKeyControl**:
|
|
36
|
+
|
|
37
|
+
- Renders checkboxes for "Keyword", "Title", "Author", and "Subject".
|
|
38
|
+
- Reads the `qIndex` URL parameter (e.g., `?qIndex=keyword,title`).
|
|
39
|
+
- Sets the "Keyword" and "Title" checkboxes as checked based on the example `qIndex`.
|
|
40
|
+
- If the user clicks the "Author" checkbox, the component updates the URL parameter to `?qIndex=keyword,title,author`.
|
|
41
|
+
- If the user then unclicks the "Keyword" checkbox, the URL parameter becomes `?qIndex=title,author`.
|
|
42
|
+
- It also automatically resets pagination (via `usePrevNextPagination`) when the `qIndex` changes.
|
|
43
|
+
|
|
44
|
+
## Props
|
|
45
|
+
|
|
46
|
+
| Name | Type | Description | Default | Required |
|
|
47
|
+
|:----------|:------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------|:---------|
|
|
48
|
+
| `options` | `arrayOf(object)` | An array of objects defining the available search keys. Each object must have: <br>• `key` (string): The value representing the search index. <br/>• `label` (string \| node): The text or element displayed next to the checkbox. | `[]` | ✕ |
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
## How It Works
|
|
52
|
+
|
|
53
|
+
1. **Reading Search Index State:**
|
|
54
|
+
The component uses the `useQIndex` hook to read the current `qIndex` URL parameter, which is expected to be a comma-separated string of active search keys (e.g., `"keyword,title"`). It memoizes this string split into an array (`qIndexArray`).
|
|
55
|
+
|
|
56
|
+
2. **Determining Checkbox State:**
|
|
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.
|
|
58
|
+
|
|
59
|
+
3. **Rendering Checkboxes:**
|
|
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.
|
|
61
|
+
|
|
62
|
+
4. **Handling Changes:**
|
|
63
|
+
When a checkbox's state is changed by the user:
|
|
64
|
+
|
|
65
|
+
* The `onChange` handler determines if the key should be added or removed from the list of active keys.
|
|
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.
|
|
68
|
+
|
|
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.
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
|
|
4
|
+
import isEqual from 'lodash/isEqual';
|
|
5
|
+
|
|
6
|
+
import { Checkbox } from '@folio/stripes/components';
|
|
7
|
+
|
|
8
|
+
import { usePrevNextPagination, useQIndex } from '../hooks';
|
|
9
|
+
|
|
10
|
+
import css from '../../../styles/SearchKeyControl.css';
|
|
11
|
+
|
|
12
|
+
/*
|
|
13
|
+
IMPORTANT -- This component is controlled by the qIndex, rather than the other way around.
|
|
14
|
+
It should inject values in/remove values from the qIndex, but not replace the entire thing.
|
|
15
|
+
*/
|
|
16
|
+
const SearchKeyControl = ({
|
|
17
|
+
options = []
|
|
18
|
+
}) => {
|
|
19
|
+
// This component expects a qIndex comprising of a comma separated list
|
|
20
|
+
const [qIndex, setQIndex] = useQIndex();
|
|
21
|
+
const qIndexArray = useMemo(() => qIndex?.split(',')?.map(index => index.trim()) ?? [], [qIndex]);
|
|
22
|
+
|
|
23
|
+
// Memoise this process so keyState changes if and only if options/qIndex change
|
|
24
|
+
const createKeyState = useCallback(() => (
|
|
25
|
+
options.reduce((acc, curr) => {
|
|
26
|
+
acc[curr.key] = { inUse: qIndexArray?.includes(curr.key), label: curr.label ?? curr.key };
|
|
27
|
+
return acc;
|
|
28
|
+
}, {})
|
|
29
|
+
), [options, qIndexArray]);
|
|
30
|
+
|
|
31
|
+
const [keyState, setKeyState] = useState(createKeyState());
|
|
32
|
+
|
|
33
|
+
// Keep keyState up to date as options/qIndex change
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
const newKeyState = createKeyState();
|
|
36
|
+
if (!isEqual(newKeyState, keyState)) {
|
|
37
|
+
setKeyState(newKeyState);
|
|
38
|
+
}
|
|
39
|
+
}, [createKeyState, keyState]);
|
|
40
|
+
|
|
41
|
+
// Check to see if page param exists, and if it does then changing qIndex should reset it
|
|
42
|
+
const { currentPage, resetPage } = usePrevNextPagination({ defaultToPageOne: false });
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<>
|
|
46
|
+
<div className={css.searchKeyControlContainer}>
|
|
47
|
+
{
|
|
48
|
+
Object.entries(keyState).map(([key, value]) => {
|
|
49
|
+
/* At this point we have "key" corresponding to a searchKey option,
|
|
50
|
+
* and "value" an object of the shape
|
|
51
|
+
{
|
|
52
|
+
inUse: a bool determining if it is in use or not,
|
|
53
|
+
label: the label to display on the checkbox
|
|
54
|
+
}
|
|
55
|
+
*/
|
|
56
|
+
return (
|
|
57
|
+
<Checkbox
|
|
58
|
+
key={`search-key-control-${key}`}
|
|
59
|
+
checked={value?.inUse}
|
|
60
|
+
className={css.searchKeyControlElement}
|
|
61
|
+
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
|
+
}}
|
|
79
|
+
/>
|
|
80
|
+
);
|
|
81
|
+
})
|
|
82
|
+
}
|
|
83
|
+
</div>
|
|
84
|
+
</>
|
|
85
|
+
);
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
SearchKeyControl.propTypes = {
|
|
89
|
+
options: PropTypes.arrayOf(PropTypes.shape({
|
|
90
|
+
label: PropTypes.oneOfType([
|
|
91
|
+
PropTypes.string,
|
|
92
|
+
PropTypes.node
|
|
93
|
+
]).isRequired,
|
|
94
|
+
key: PropTypes.string.isRequired
|
|
95
|
+
}))
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
export default SearchKeyControl;
|