@k-int/stripes-kint-components 5.20.1 → 5.22.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,22 @@
1
+ # [5.22.0](https://gitlab.com/knowledge-integration/folio/stripes-kint-components/compare/v5.21.0...v5.22.0) (2025-06-13)
2
+
3
+
4
+ ### Features
5
+
6
+ * Customisable NoResultsMessage ([f4b18d8](https://gitlab.com/knowledge-integration/folio/stripes-kint-components/commit/f4b18d809ba83e7d7f6141b2ace09f3e32e18b02))
7
+
8
+ # [5.21.0](https://gitlab.com/knowledge-integration/folio/stripes-kint-components/compare/v5.20.1...v5.21.0) (2025-06-06)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * useKiwtSASQuery use state ([7e4f89c](https://gitlab.com/knowledge-integration/folio/stripes-kint-components/commit/7e4f89c6fb4b0a7a64331f55a4647c5c6a9c3464))
14
+
15
+
16
+ ### Features
17
+
18
+ * useSettingSection configurability ([8e7b724](https://gitlab.com/knowledge-integration/folio/stripes-kint-components/commit/8e7b724f442c440b1a3d5b36b95300e4417b4d4e))
19
+
1
20
  ## [5.20.1](https://gitlab.com/knowledge-integration/folio/stripes-kint-components/compare/v5.20.0...v5.20.1) (2025-05-14)
2
21
 
3
22
 
@@ -66,7 +66,7 @@ const NoResultsMessage = _ref => {
66
66
  iconRootClass: _NoResultsMessage.default.noResultsMessageIcon
67
67
  }), /*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
68
68
  className: _NoResultsMessage.default.noResultsMessageLabel,
69
- children: userLabel ?? label
69
+ children: userLabel ?? labelOverrides.noResultsLabel ?? label
70
70
  })]
71
71
  }), !filterPaneIsVisible && /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Button, {
72
72
  buttonClass: _NoResultsMessage.default.noResultsMessageButton,
@@ -46,6 +46,7 @@ const SASQLookupComponent = /*#__PURE__*/(0, _react.forwardRef)((props, ref) =>
46
46
  },
47
47
  mainPaneProps = {},
48
48
  mclProps = {},
49
+ noResultsProps = {},
49
50
  noSearchField,
50
51
  persistedPanesetProps = {},
51
52
  queryParameterGenerator = _utils.generateKiwtQuery,
@@ -260,6 +261,7 @@ const SASQLookupComponent = /*#__PURE__*/(0, _react.forwardRef)((props, ref) =>
260
261
  intlKey: passedIntlKey,
261
262
  intlNS: passedIntlNS,
262
263
  labelOverrides: labelOverrides,
264
+ noResultsProps: noResultsProps,
263
265
  query: query,
264
266
  rowNavigation: rowNavigation,
265
267
  toggleFilterPane: toggleFilterPane,
@@ -27,6 +27,7 @@ const TableBody = _ref => {
27
27
  formatter = {},
28
28
  ...mclProps
29
29
  } = {},
30
+ noResultsProps = {},
30
31
  onSort,
31
32
  path,
32
33
  resultColumns,
@@ -82,6 +83,7 @@ const TableBody = _ref => {
82
83
  } = _ref2;
83
84
  return item.id === match?.params?.id;
84
85
  }, [match?.params?.id]);
86
+ const NoResultsComponent = (0, _react.useMemo)(() => noResultsProps?.component ?? _NoResultsMessage.default, [noResultsProps?.component]);
85
87
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.MultiColumnList, {
86
88
  autosize: true,
87
89
  columnMapping: columnMapping,
@@ -90,7 +92,7 @@ const TableBody = _ref => {
90
92
  ,
91
93
  hasMargin: true,
92
94
  interactive: rowNavigation,
93
- isEmptyMessage: /*#__PURE__*/(0, _jsxRuntime.jsx)(_NoResultsMessage.default, {
95
+ isEmptyMessage: /*#__PURE__*/(0, _jsxRuntime.jsx)(NoResultsComponent, {
94
96
  error,
95
97
  filterPaneIsVisible: filterPaneVisible,
96
98
  intlKey: passedIntlKey,
@@ -99,7 +101,8 @@ const TableBody = _ref => {
99
101
  isLoading,
100
102
  labelOverrides,
101
103
  searchTerm: query.query,
102
- toggleFilterPane
104
+ toggleFilterPane,
105
+ ...noResultsProps // Anything passed in directly takes precedence
103
106
  }),
104
107
  isSelected: isSelected,
105
108
  onHeaderClick: onSort,
@@ -13,7 +13,8 @@ const locationQuerySetter = _ref => {
13
13
  let {
14
14
  location,
15
15
  history,
16
- nsValues
16
+ nsValues,
17
+ state
17
18
  } = _ref;
18
19
  const {
19
20
  pathname,
@@ -24,7 +25,13 @@ const locationQuerySetter = _ref => {
24
25
  // Do not push to history if the url didn't change
25
26
  // https://issues.folio.org/browse/STSMACOM-637
26
27
  if (`${pathname}${search}` !== url) {
27
- history.push(url);
28
+ // Only PUSH to history if we're not doing an initialisation OR changing from external-location
29
+ // This is the SASQ state being used here
30
+ if (['external-location', 'init.reset'].includes(state?.changeType)) {
31
+ history.replace(url);
32
+ } else {
33
+ history.push(url);
34
+ }
28
35
  }
29
36
  };
30
37
  const useKiwtSASQuery = () => {
@@ -42,7 +49,8 @@ const useKiwtSASQuery = () => {
42
49
  }
43
50
  const querySetter = _ref2 => {
44
51
  let {
45
- nsValues
52
+ nsValues,
53
+ ...rest
46
54
  } = _ref2;
47
55
  setQuery({
48
56
  ...query,
@@ -50,6 +58,7 @@ const useKiwtSASQuery = () => {
50
58
  qindex
51
59
  });
52
60
  locationQuerySetter({
61
+ ...rest,
53
62
  location,
54
63
  history,
55
64
  nsValues
@@ -101,7 +101,9 @@ const usePrevNextPagination = function () {
101
101
  ...urlQuery,
102
102
  page: 1
103
103
  };
104
- history.push({
104
+
105
+ // We use replace instead of push for the defaulting so we don't add unnecessary history entries
106
+ history.replace({
105
107
  pathname: location.pathname,
106
108
  search: `?${_queryString.default.stringify(newQuery)}`
107
109
  });
@@ -15,9 +15,9 @@ const useSettingSection = _ref => {
15
15
  let {
16
16
  queryParams
17
17
  } = _ref2;
18
- const queryNamespace = ['stripes-kint-components', 'useSetting', 'appSettings', queryParams, sectionName];
19
- return queryNamespace;
20
- }
18
+ return ['stripes-kint-components', 'useSetting', 'appSettings', queryParams, sectionName];
19
+ },
20
+ getMutateNamespaceGenerator = () => ['stripes-kint-components', 'useSetting', 'putSetting', sectionName]
21
21
  } = _ref;
22
22
  const ky = (0, _core.useOkapiKy)();
23
23
  const queryParams = (0, _utils.generateKiwtQueryParams)({
@@ -32,18 +32,22 @@ const useSettingSection = _ref => {
32
32
  stats: false
33
33
  }, {});
34
34
  const {
35
- data: settings = []
35
+ data: settings = [],
36
+ ...settingsQuery
36
37
  } = (0, _reactQuery.useQuery)(getQueryNamespaceGenerator({
37
38
  queryParams
38
39
  }), () => ky(`${settingEndpoint}?${queryParams?.join('&')}`).json());
39
40
  const {
40
- mutateAsync: putSetting
41
- } = (0, _reactQuery.useMutation)(['stripes-kint-components', 'useSetting', 'putSetting', sectionName], data => ky.put(`${settingEndpoint}${data.id ? '/' + data.id : ''}`, {
41
+ mutateAsync: putSetting,
42
+ ...settingsMutate
43
+ } = (0, _reactQuery.useMutation)(getMutateNamespaceGenerator(), data => ky.put(`${settingEndpoint}${data.id ? '/' + data.id : ''}`, {
42
44
  json: data
43
45
  }));
44
46
  return {
45
47
  handleSubmit: putSetting,
46
- settings
48
+ settings,
49
+ settingsQuery,
50
+ settingsMutate
47
51
  };
48
52
  };
49
53
  var _default = exports.default = useSettingSection;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@k-int/stripes-kint-components",
3
- "version": "5.20.1",
3
+ "version": "5.22.0",
4
4
  "description": "Stripes Component library for K-Int specific applications",
5
5
  "sideEffects": [
6
6
  "*.css"
@@ -0,0 +1,80 @@
1
+ # FormattedKintMessage
2
+
3
+ A wrapper component around `react-intl`'s `FormattedMessage` that enhances internationalization capabilities by providing automatic key resolution, direct value overrides, and flexible fallback messaging. **FormattedKintMessage** is designed to work within the `k-int` internationalization ecosystem, leveraging `useIntlKey` for intelligent message key lookup via `useIntlKey` and other intl hooks. [See intl hooks for details](https://gitlab.com/knowledge-integration/folio/stripes-kint-components/-/tree/main/src/lib/hooks/intlHooks)
4
+
5
+ ## Basic Usage
6
+
7
+ Below is an example demonstrating the most basic use of **FormattedKintMessage** to display translated text using automatic key resolution.
8
+
9
+ ```jsx
10
+ import { FormattedKintMessage } from '@k-int/stripes-kint-components';
11
+
12
+ const MyInternationalizedComponent = () => (
13
+ <div>
14
+ {/* Resolves key using intlKey/intlNS from context or props */}
15
+ <FormattedKintMessage id="title.greeting" intlKey="myModule" intlNS="myApp" />
16
+ </div>
17
+ );
18
+
19
+ export default MyInternationalizedComponent;
20
+ ```
21
+
22
+ ### Explanation
23
+
24
+ * **`id`**: The relative ID for your message, which will be prefixed by the resolved `intlKey`.
25
+
26
+ * **`intlKey` / `intlNS`**: These props are used by `useIntlKey` to determine the base prefix for your message ID, allowing for flexible internationalization key management.
27
+
28
+ ---
29
+
30
+ ## Override and Fallback Message Usage
31
+
32
+ **`overrideValue`** and **`fallbackMessage`** both provide ways to control the message displayed, but they serve distinct purposes:
33
+
34
+ * **`overrideValue`**: Use this when you want to **explicitly define** the content to be displayed, entirely bypassing the translation lookup process for the `id` prop.
35
+ * The `overrideValue` can be a direct string (e.g., "Custom Text") or another existing `react-intl` message key (e.g., "common.cancelButton").
36
+ * If it's a key, `FormattedKintMessage` will attempt to format that key; otherwise, it displays the string literally.
37
+ * This is useful for dynamic content that might not have a static translation key or for providing specific, hardcoded text for a particular instance.
38
+ * As of `stripes-kint-components ^3.0.0`, `overrideValue` must be a string.
39
+
40
+ * **`fallbackMessage`**: Use this when you expect a translation to exist for your primary `id`, but you want a **graceful, silent fallback** if that translation is unexpectedly missing.
41
+ * This prevents `react-intl`'s default console warnings for missing messages.
42
+ * The `fallbackMessage` can also be a direct string or another existing `react-intl` message key.
43
+ * It acts as a safety net, ensuring *something* is displayed even if the primary translation isn't available.
44
+
45
+ **In summary:**
46
+
47
+ * Choose `overrideValue` for **deliberate replacement** of the translation.
48
+
49
+ * Choose `fallbackMessage` for **graceful handling of missing translations**.
50
+
51
+ ```jsx
52
+ import { FormattedKintMessage } from '@k-int/stripes-kint-components';
53
+
54
+ const OverrideAndFallbackExample = () => (
55
+ <div>
56
+ {/* Using an overrideValue (direct string) */}
57
+ <FormattedKintMessage id="unimportant.key" overrideValue="This is a direct message." />
58
+
59
+ {/* Using an overrideValue (another existing intl key) */}
60
+ <FormattedKintMessage id="unused.key" overrideValue="someOtherIntlKey" />
61
+
62
+ {/* Using a fallbackMessage when the primary key is not found (direct string) */}
63
+ <FormattedKintMessage id="missing.message" fallbackMessage="Default message if not found." />
64
+
65
+ {/* Using a fallbackMessage that is itself an existing intl key */}
66
+ <FormattedKintMessage id="another.missing.message" fallbackMessage="common.error.generic" />
67
+ </div>
68
+ );
69
+ ```
70
+
71
+ ## Props
72
+
73
+ | Prop | Type | Required | Description |
74
+ | :----------------------- | :--------------------------- | :------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
75
+ | `fallbackMessage` | `string` | ✕ | A message or `react-intl` key to display if the primary `id` (after `intlKey` resolution) does not have a corresponding translation. This acts as a silent `defaultMessage` without console warnings. If it is a key, it will be formatted; otherwise, it will be displayed literally. |
76
+ | `id` | `string` | ✓ | The relative message ID to be translated. This ID is combined with the `intlKey` (resolved via `useIntlKey`) to form the full translation key. |
77
+ | `intlKey` | `string` | ✕ | A base internationalization key passed to the internal `useIntlKey` hook to resolve the full message path. |
78
+ | `intlNS` | `string` | ✕ | An internationalization namespace passed to the internal `useIntlKey` hook for resolving the full message path. |
79
+ | `overrideValue` | `string` | ✕ | A string value that, if present, takes precedence over all other translation logic. It can be a direct string to display or another `react-intl` message `id`. If it's a key, it will be formatted; otherwise, it will be displayed literally. **As of `stripes-kint-components ^3.0.0`, this must be a string.** |
80
+ | `...formattedMessageProps` | `object` | ✕ | Any additional props supported by `react-intl`'s original `FormattedMessage` component, such as `values` for injecting dynamic content into the message, `tagName`, etc. |
@@ -50,7 +50,7 @@ const NoResultsMessage = ({
50
50
  iconRootClass={css.noResultsMessageIcon}
51
51
  />
52
52
  }
53
- <span className={css.noResultsMessageLabel}>{userLabel ?? label}</span>
53
+ <span className={css.noResultsMessageLabel}>{userLabel ?? labelOverrides.noResultsLabel ?? label}</span>
54
54
  </div>
55
55
  {/* If the filter pane is closed we show a button that toggles filter pane */}
56
56
  {!filterPaneIsVisible && (
@@ -0,0 +1,81 @@
1
+ # NoResultsMessage
2
+
3
+ A component that displays messages based on the state of data fetching and user interaction within lookup views.
4
+ **NoResultsMessage** dynamically adjusts its icon and text to indicate loading states, no results found after a search, no search terms entered, or an error during data retrieval.
5
+ It also provides a button to toggle the visibility of a filter pane. Generally called within context of `SASQLookupComponent -> TableBody`
6
+
7
+ ## Basic Usage
8
+
9
+ Below is an example demonstrating how **NoResultsMessage** might be used within a component that manages data fetching, such as `SASQTableBody` or directly within a `MultiColumnList`'s `isEmptyMessage` prop.
10
+
11
+ ```jsx
12
+ import { useState } from 'react';
13
+ import NoResultsMessage from '@k-int/stripes-kint-components/lib/NoResultsMessage';
14
+
15
+ const MyDataView = () => {
16
+ const [isLoading, setIsLoading] = useState(true);
17
+ const [searchTerm, setSearchTerm] = useState('');
18
+ const [filterPaneIsVisible, setFilterPaneIsVisible] = useState(true);
19
+ const [isError, setIsError] = useState(false);
20
+ const [error, setError] = useState(null);
21
+
22
+ const toggleFilterPane = () => setFilterPaneIsVisible(prev => !prev);
23
+
24
+ // Simulate data fetching
25
+ useState(() => {
26
+ setTimeout(() => {
27
+ setIsLoading(false);
28
+ // setSearchTerm('example'); // Uncomment to simulate a search term
29
+ // setIsError(true); // Uncomment to simulate an error
30
+ // setError({ message: 'Failed to fetch data' });
31
+ }, 2000);
32
+ }, []);
33
+
34
+ return (
35
+ <div style={{ padding: '20px', border: '1px solid #ccc', minHeight: '200px' }}>
36
+ <h3>Data Display Area</h3>
37
+ <NoResultsMessage
38
+ isLoading={isLoading}
39
+ searchTerm={searchTerm}
40
+ filterPaneIsVisible={filterPaneIsVisible}
41
+ toggleFilterPane={toggleFilterPane}
42
+ isError={isError}
43
+ error={error}
44
+ // label="Custom No Results Text" // Optional: provide custom label
45
+ // icon="info" // Optional: provide custom icon
46
+ // labelOverrides={{ showFilters: 'Open Filters Panel' }} // Optional: override specific labels
47
+ />
48
+ </div>
49
+ );
50
+ };
51
+
52
+ export default MyDataView;
53
+ ```
54
+
55
+ ### Explanation
56
+
57
+ * **isLoading:** Set to `true` when data is being fetched, displaying a loading message and spinner icon.
58
+
59
+ * **searchTerm:** Indicates whether a search term has been entered by the user. Influences the displayed message for "no results" vs. "no terms entered."
60
+
61
+ * **filterPaneIsVisible:** Controls the visibility of the "Show Filters" button.
62
+
63
+ * **toggleFilterPane:** A callback function triggered by the "Show Filters" button, typically to open the filter pane.
64
+
65
+ * **isError** and **error:** Used to display an error icon and the error message if the data fetching operation fails.
66
+
67
+ ## Props
68
+
69
+ | **Prop** | **Type** | **Required** | **Description** |
70
+ |-----------------------|--------------------------|--------------|-------------------------------------------------------------------------------------------------------------------------------------------------------|
71
+ | `filterPaneIsVisible` | `boolean` | ✓ | Indicates if the associated filter pane is currently visible. Controls the display of the "Show Filters" button. |
72
+ | `toggleFilterPane` | `() => void` | ✓ | Callback function invoked when the "Show Filters" button is clicked. Typically used to toggle the filter pane's visibility. |
73
+ | `error` | `object` | ✕ | An error object (e.g., from `react-query`) containing details of a failed request. Its `message` property will be displayed if `isError` is `true`. |
74
+ | `icon` | `string` | ✕ | An optional custom icon string (from `@folio/stripes/components/Icon`) to override the default icon based on the message state. |
75
+ | `intlKey` | `string` | ✕ | A base internationalization key used by the internal `useKintIntl` hook to resolve localized messages. |
76
+ | `intlNS` | `string` | ✕ | An internationalization namespace used by the internal `useKintIntl` hook. |
77
+ | `isLoading` | `boolean` | ✕ | When `true`, indicates that data is currently being loaded, displaying a loading message. |
78
+ | `isError` | `boolean` | ✕ | When `true`, indicates an error occurred during data fetching, displaying an error message. |
79
+ | `label` | `string`, `node`, `func` | ✕ | An optional custom label to override all default message content. |
80
+ | `labelOverrides` | `object` | ✕ | An object for overriding specific parts of the default labels, such as `{ noResultsLabel: 'No records found' }` or `{ showFilters: 'View Filters' }`. |
81
+ | `searchTerm` | `string` | ✕ | The current search query string. Used to differentiate between "no results for search" and "no search term entered" messages. |
@@ -57,29 +57,30 @@ In this example, **SASQLookupComponent**:
57
57
 
58
58
  ## Props
59
59
 
60
- | Name | Type | Description | Default | Required |
61
- |---------------------------------|-------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------|----------|
62
- | `id` | `string` | A unique identifier for the lookup instance. Used to generate unique keys for query state, persistent paneset, and filter pane visibility in local storage. | | ✓ |
63
- | `fetchParameters` | `object` | An object that defines the endpoints and SASQ configuration used for fetching data. It must include:<br><br>• **endpoint:** The URL used to fetch the main data set.<br>• **SASQ_MAP:** An object containing settings for search, filter, pagination (e.g., `searchKey`, `perPage`, etc.). Additional keys (such as `itemEndpoint`) can also be provided if needed. | `{}` | ✓ |
64
- | `FilterComponent` | `func` or `node` | A custom component used to render additional filtering options in the filter pane. This component receives props for managing active filters, search state, and resetting filters. | `() => null` | ✕ |
65
- | `FilterPaneHeaderComponent` | `func` or `node` | A component rendered at the top of the filter pane. Use this to customize the header area of the filter pane. | `() => null` | ✕ |
66
- | `filterPaneProps` | `object` | Additional props to be passed to the filter pane. These props can customize the appearance and behavior of the filter pane (such as first or last menu elements). | `{}` | ✕ |
67
- | `intlKey` | `string` | A base internationalization key used by the internal `useKintIntl` hook to resolve localized messages (e.g., for labels or pane titles). | | ✕ |
68
- | `intlNS` | `string` | An internationalization namespace used by the internal `useKintIntl` hook. | | ✕ |
69
- | `labelOverrides` | `object` | An object for overriding default labels, such as the text for displaying the count of found values. | `{}` | ✕ |
70
- | `lookupQueryNamespaceGenerator` | `func` | A function that generates a query namespace (an array) for `react-query` based on provided parameters such as `currentPage`, `namespace`, `id`, `query`, etc. This ensures that queries are uniquely identified. | See default implementation in source code | ✕ |
71
- | `mainPaneProps` | `object` | Additional props to be passed to the main pane containing the lookup results. This can include custom menu elements or styling. | `{}` | ✕ |
72
- | `mclProps` | `object` | Custom properties to be passed to the underlying Multi-Column List (MCL) component. These properties are merged with pagination props derived from `usePrevNextPagination`. | `{}` | ✕ |
73
- | `noSearchField` | `bool` | When set to `true`, the search field is not rendered in the filter pane. Use this if you want to disable search input. | `false` | ✕ |
74
- | `persistedPanesetProps` | `object` | Additional props to be passed to the **PersistedPaneset** component, which manages the persistent layout of the filter and main panes. | `{}` | ✕ |
75
- | `queryParameterGenerator` | `func` | A function that takes the SASQ_MAP configuration and current query values to generate an array of query parameters for the fetch call. Defaults to `generateKiwtQuery`. | `generateKiwtQuery` | ✕ |
76
- | `RenderBody` | `func` or `node` | A component used to render the body of the main pane (typically a table). If not provided, the internal **TableBody** component is used. | `TableBody` | ✕ |
77
- | `rowNavigation` | `bool` | Flag to enable or disable row navigation. When enabled, clicking on a row triggers navigation to a detailed view. | `true` | ✕ |
78
- | `sasqProps` | `object` | Additional props to override or extend the default SASQ query behavior. These props can include custom query values, setters, or getters. | | ✕ |
79
- | `searchableIndexes` | `arrayOf(object)` | An array of objects defining the options for the search index selection component (`SearchKeyControl`), rendered below the search field. Each object typically has `value` and `label` keys. If empty or omitted, the qindex checkboxes are not shown. | `[]` | ✕ |
80
- | `searchFieldAriaLabel` | `string` | Accessibility label for the search field. | | ✕ |
81
- | `searchFieldProps` | `object` | Additional props to be passed to the **SearchField** component for customizing its behavior or styling. | | ✕ |
82
- | `children` | `node`, `func` | Optional children to be rendered below the main pane. These are rendered within the **PersistedPaneset** container. | | ✕ |
60
+ | Name | Type | Description | Default | Required |
61
+ |---------------------------------|------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------|----------|
62
+ | `id` | `string` | A unique identifier for the lookup instance. Used to generate unique keys for query state, persistent paneset, and filter pane visibility in local storage. | | ✓ |
63
+ | `fetchParameters` | `object` | An object that defines the endpoints and SASQ configuration used for fetching data. It must include:<br><br>• **endpoint:** The URL used to fetch the main data set.<br>• **SASQ_MAP:** An object containing settings for search, filter, pagination (e.g., `searchKey`, `perPage`, etc.). Additional keys (such as `itemEndpoint`) can also be provided if needed. | `{}` | ✓ |
64
+ | `FilterComponent` | `func` or `node` | A custom component used to render additional filtering options in the filter pane. This component receives props for managing active filters, search state, and resetting filters. | `() => null` | ✕ |
65
+ | `FilterPaneHeaderComponent` | `func` or `node` | A component rendered at the top of the filter pane. Use this to customize the header area of the filter pane. | `() => null` | ✕ |
66
+ | `filterPaneProps` | `object` | Additional props to be passed to the filter pane. These props can customize the appearance and behavior of the filter pane (such as first or last menu elements). | `{}` | ✕ |
67
+ | `intlKey` | `string` | A base internationalization key used by the internal `useKintIntl` hook to resolve localized messages (e.g., for labels or pane titles). | | ✕ |
68
+ | `intlNS` | `string` | An internationalization namespace used by the internal `useKintIntl` hook. | | ✕ |
69
+ | `labelOverrides` | `object` | An object for overriding default labels, such as the text for displaying the count of found values. | `{}` | ✕ |
70
+ | `lookupQueryNamespaceGenerator` | `func` | A function that generates a query namespace (an array) for `react-query` based on provided parameters such as `currentPage`, `namespace`, `id`, `query`, etc. This ensures that queries are uniquely identified. | See default implementation in source code | ✕ |
71
+ | `mainPaneProps` | `object` | Additional props to be passed to the main pane containing the lookup results. This can include custom menu elements or styling. | `{}` | ✕ |
72
+ | `mclProps` | `object` | Custom properties to be passed to the underlying Multi-Column List (MCL) component. These properties are merged with pagination props derived from `usePrevNextPagination`. | `{}` | ✕ |
73
+ | `noResultsProps` | `object` | Props to be passed down to the [NoResultsMessage](https://gitlab.com/knowledge-integration/folio/stripes-kint-components/-/tree/main/src/lib/NoResultsMessage) splash screen component rendered when no results are present from the query. If `component` is passed, this will be rendered with the props available to `NoResultsMessage` | {} | ✕ |
74
+ | `noSearchField` | `bool` | When set to `true`, the search field is not rendered in the filter pane. Use this if you want to disable search input. | `false` | ✕ |
75
+ | `persistedPanesetProps` | `object` | Additional props to be passed to the **PersistedPaneset** component, which manages the persistent layout of the filter and main panes. | `{}` | ✕ |
76
+ | `queryParameterGenerator` | `func` | A function that takes the SASQ_MAP configuration and current query values to generate an array of query parameters for the fetch call. Defaults to `generateKiwtQuery`. | `generateKiwtQuery` | ✕ |
77
+ | `RenderBody` | `func` or `node` | A component used to render the body of the main pane (typically a table). If not provided, the internal **TableBody** component is used. | `TableBody` | ✕ |
78
+ | `rowNavigation` | `bool` | Flag to enable or disable row navigation. When enabled, clicking on a row triggers navigation to a detailed view. | `true` | ✕ |
79
+ | `sasqProps` | `object` | Additional props to override or extend the default SASQ query behavior. These props can include custom query values, setters, or getters. | | ✕ |
80
+ | `searchableIndexes` | `arrayOf(object)` | An array of objects defining the options for the search index selection component (`SearchKeyControl`), rendered below the search field. Each object typically has `value` and `label` keys. If empty or omitted, the qindex checkboxes are not shown. | `[]` | ✕ |
81
+ | `searchFieldAriaLabel` | `string` | Accessibility label for the search field. | | ✕ |
82
+ | `searchFieldProps` | `object` | Additional props to be passed to the **SearchField** component for customizing its behavior or styling. | | ✕ |
83
+ | `children` | `node`, `func` | Optional children to be rendered below the main pane. These are rendered within the **PersistedPaneset** container. | | ✕ |
83
84
  Below is an additional section for **SASQLookupComponent** documentation that describes the available functionality on the component's ref along with a worked example.
84
85
 
85
86
 
@@ -54,6 +54,7 @@ const SASQLookupComponent = forwardRef((props, ref) => {
54
54
  },
55
55
  mainPaneProps = {},
56
56
  mclProps = {},
57
+ noResultsProps = {},
57
58
  noSearchField,
58
59
  persistedPanesetProps = {},
59
60
  queryParameterGenerator = generateKiwtQuery, // Expects a function which accepts SASQ_MAP and nsValues
@@ -304,6 +305,7 @@ const SASQLookupComponent = forwardRef((props, ref) => {
304
305
  intlKey={passedIntlKey}
305
306
  intlNS={passedIntlNS}
306
307
  labelOverrides={labelOverrides}
308
+ noResultsProps={noResultsProps}
307
309
  query={query}
308
310
  rowNavigation={rowNavigation}
309
311
  toggleFilterPane={toggleFilterPane}
@@ -61,24 +61,25 @@ export default MyTableView;
61
61
 
62
62
  ## Props
63
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`. |
64
+ | Prop | Type | Required | Description |
65
+ |---------------------|--------------------------------------------------|----------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
66
+ | `data` | `{ totalRecords: number, results: object[] }` | ✓ | Data to display. `results` contains the items, `totalRecords` sets pagination bounds. |
67
+ | `error` | `object` | | Error object displayed in `NoResultsMessage`. |
68
+ | `fetchNextPage` | `({ pageParam: number }) => void` | ✓ | Callback to load more data. `pageParam` indicates the next page index. |
69
+ | `filterPaneVisible` | `boolean` | | Toggles filter pane visibility in `NoResultsMessage`. |
70
+ | `intlKey` | `string` | | Base key for internationalizing labels. See `useKintIntl`. |
71
+ | `intlNS` | `string` | ✕ | Namespace for internationalization. See `useKintIntl`. |
72
+ | `isError` | `boolean` | ✕ | Shows error state in `NoResultsMessage`. |
73
+ | `isLoading` | `boolean` | ✕ | Shows loading state in `NoResultsMessage`. |
74
+ | `labelOverrides` | `object` | ✕ | Overrides default labels (e.g., `{ noResultsFound: 'No items' }`). |
75
+ | `match` | `object` | ✕ | React Router `match` object. Used to highlight the selected row via URL `id` param. |
76
+ | `mclProps` | `object` | ✕ | Props passed to the underlying [MultiColumnList](https://github.com/folio-org/stripes-components/tree/master/lib/MultiColumnList). Example: `formatter`, `id`. |
77
+ | `noResultsProps` | `object` | ✕ | Props to be passed down to the [NoResultsMessage](https://gitlab.com/knowledge-integration/folio/stripes-kint-components/-/tree/main/src/lib/NoResultsMessage) splash screen component rendered when no results are present from the query. If `component` is passed, this will be rendered with the props available to `NoResultsMessage` |
78
+ | `path` | `string` | | Base path for row URLs. Each row links to `{path}/{rowData.id}`. |
79
+ | `query` | `object` | | Current query state (e.g., `{ sort: 'name', query: 'search term' }`). Used for sorting and empty state messages. |
80
+ | `resultColumns` | `Array<{ propertyPath: string, label: string }>` | | Columns to render. `propertyPath` accesses the data field, `label` sets the header. |
81
+ | `rowNavigation` | `boolean` | ✕ | Enables navigation to detail views on row click. Default: `true`. |
82
+ | `toggleFilterPane` | `() => void` | ✕ | Callback to toggle filter pane. Used in `NoResultsMessage`. |
82
83
 
83
84
  ## Key Features
84
85
 
@@ -1,4 +1,4 @@
1
- import { useCallback } from 'react';
1
+ import { useCallback, useMemo } from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
 
4
4
  import { useHistory, useLocation } from 'react-router-dom';
@@ -23,6 +23,7 @@ const TableBody = ({
23
23
  formatter = {},
24
24
  ...mclProps
25
25
  } = {},
26
+ noResultsProps = {},
26
27
  onSort,
27
28
  path,
28
29
  resultColumns,
@@ -76,6 +77,7 @@ const TableBody = ({
76
77
  }, [getRowUrl, history, rowNavigation]);
77
78
 
78
79
  const isSelected = useCallback(({ item }) => item.id === match?.params?.id, [match?.params?.id]);
80
+ const NoResultsComponent = useMemo(() => noResultsProps?.component ?? NoResultsMessage, [noResultsProps?.component]);
79
81
 
80
82
  return (
81
83
  <MultiColumnList
@@ -86,7 +88,7 @@ const TableBody = ({
86
88
  hasMargin
87
89
  interactive={rowNavigation}
88
90
  isEmptyMessage={
89
- <NoResultsMessage
91
+ <NoResultsComponent
90
92
  {...{
91
93
  error,
92
94
  filterPaneIsVisible: filterPaneVisible,
@@ -96,7 +98,8 @@ const TableBody = ({
96
98
  isLoading,
97
99
  labelOverrides,
98
100
  searchTerm: query.query,
99
- toggleFilterPane
101
+ toggleFilterPane,
102
+ ...noResultsProps, // Anything passed in directly takes precedence
100
103
  }}
101
104
  />
102
105
  }
@@ -4,14 +4,22 @@ import { useLocation, useHistory } from 'react-router-dom';
4
4
  import buildUrl from '../utils/buildUrl';
5
5
  import useQindex from './useQIndex';
6
6
 
7
- const locationQuerySetter = ({ location, history, nsValues }) => {
7
+ const locationQuerySetter = ({ location, history, nsValues, state }) => {
8
8
  const { pathname, search } = location;
9
9
  const url = buildUrl(location, nsValues);
10
10
 
11
11
  // Do not push to history if the url didn't change
12
12
  // https://issues.folio.org/browse/STSMACOM-637
13
- if (`${pathname}${search}` !== url) {
14
- history.push(url);
13
+ if (
14
+ `${pathname}${search}` !== url
15
+ ) {
16
+ // Only PUSH to history if we're not doing an initialisation OR changing from external-location
17
+ // This is the SASQ state being used here
18
+ if (['external-location', 'init.reset'].includes(state?.changeType)) {
19
+ history.replace(url);
20
+ } else {
21
+ history.push(url);
22
+ }
15
23
  }
16
24
  };
17
25
 
@@ -27,9 +35,9 @@ const useKiwtSASQuery = () => {
27
35
  setQuery({ ...query, qindex });
28
36
  }
29
37
 
30
- const querySetter = ({ nsValues }) => {
38
+ const querySetter = ({ nsValues, ...rest }) => {
31
39
  setQuery({ ...query, ...nsValues, qindex });
32
- locationQuerySetter({ location, history, nsValues });
40
+ locationQuerySetter({ ...rest, location, history, nsValues });
33
41
  };
34
42
  return { query, queryGetter, querySetter };
35
43
  };
@@ -115,7 +115,8 @@ const usePrevNextPagination = ({
115
115
  page: 1
116
116
  };
117
117
 
118
- history.push({
118
+ // We use replace instead of push for the defaulting so we don't add unnecessary history entries
119
+ history.replace({
119
120
  pathname: location.pathname,
120
121
  search: `?${queryString.stringify(newQuery)}`
121
122
  });
@@ -1,14 +1,14 @@
1
1
  # useSettingSection
2
2
 
3
- A custom hook that retrieves settings data for a given section and provides a handler to update individual settings. It leverages `react-query` to perform asynchronous data fetching and mutations against a specified settings API endpoint.
3
+ A custom React hook designed for fetching and updating settings data from an API. It leverages `react-query` for robust asynchronous data management and provides flexible control over query and mutation keys.
4
4
 
5
5
  ## Overview
6
6
 
7
- `useSettingSection` is designed to:
8
- - **Fetch Settings:**
9
- Use `react-query` to request settings that belong to a specific section, based on a filter that matches the provided `sectionName`. The results are sorted by the setting key.
10
- - **Update Settings:**
11
- Provide a mutation handler (`handleSubmit`) to update a setting. The mutation performs an HTTP PUT request to the API endpoint with the setting data.
7
+ `useSettingSection` simplifies interaction with a settings API by:
8
+
9
+ * **Fetching Settings:** It retrieves settings for a specific `sectionName`, automatically filtering and sorting the results. Data is cached and managed efficiently by `react-query`.
10
+ * **Updating Settings:** It provides a mutation handler (`handleSubmit`) to send updates for individual settings, performing an HTTP PUT request to your specified endpoint.
11
+ * **Flexible Key Generation:** Offers optional functions to customize the `react-query` keys for both data fetching and mutations, allowing for fine-grained cache control and query invalidation.
12
12
 
13
13
  ## Basic Usage
14
14
 
@@ -38,17 +38,18 @@ const SettingsEditor = ({ sectionName, settingEndpoint }) => {
38
38
 
39
39
  ## Parameters
40
40
 
41
- | Name | Type | Description | Required |
42
- |------------------|----------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------|
43
- | `sectionName` | `string` | The name of the settings section to fetch. This is used to filter the settings by matching the section property in the API query. | ✓ |
44
- | `settingEndpoint`| `string` | The base URL endpoint for the settings API. The hook appends query parameters and, in the case of an update, the setting's ID. | ✓ |
41
+ | Name | Type | Description | Required | Default Value |
42
+ | :----------------------------- | :--------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
43
+ | `sectionName` | `string` | The name of the settings section to fetch. This is used in the `generateKiwtQueryParams` call to filter settings by their `section` property. | ✓ | None |
44
+ | `settingEndpoint` | `string` | The base URL endpoint for the settings API (e.g., `/settings-storage/settings`). The hook appends query parameters for fetching and, in the case of an update, the setting's ID for PUT requests. | ✓ | None |
45
+ | `getQueryNamespaceGenerator` | `function` | An optional function that generates the `react-query` key for the settings query. It receives an object `{ queryParams }` as an argument, where `queryParams` is the array of generated Kiwt query parameters. **Customize this for specific caching behaviors or if you need to append additional identifiers to your query key.** | ✕ | `({ queryParams }) => ['stripes-kint-components', 'useSetting', 'appSettings', queryParams, sectionName]` |
46
+ | `getMutateNamespaceGenerator` | `function` | An optional function that generates the `react-query` key for the mutation. **Customize this if you need a specific key for your mutation, for example, to differentiate it from other mutations or target specific invalidations.** | ✕ | `() => ['stripes-kint-components', 'useSetting', 'putSetting', sectionName]` |
45
47
 
46
48
  ## Returns
47
49
 
48
50
  An object containing:
49
51
 
50
- - **`settings`** (`array`):
51
- The array of settings retrieved from the API. If the query has not completed or returns no data, it defaults to an empty array.
52
-
53
- - **`handleSubmit`** (`function`):
54
- A mutation function that accepts a settings data object and performs an HTTP PUT request to update the setting.
52
+ * **`settings`** (`array`): The array of settings retrieved from the API. Defaults to an empty array if the query has not completed or returns no data.
53
+ * **`handleSubmit`** (`function`): A `react-query` mutation function (`mutateAsync`) that accepts a settings data object (e.g., `{ id: 'uuid', key: 'mySetting', value: 'newValue' }`) and performs an HTTP PUT request to update the setting.
54
+ * **`settingsQuery`** (`object`): The `react-query` result object from the `useQuery` hook. This provides detailed query status (e.g., `isLoading`, `isFetching`, `isError`, `data`, `error`).
55
+ * **`settingsMutate`** (`object`): The `react-query` result object from the `useMutation` hook. This provides detailed mutation status (e.g., `isLoading`, `isError`, `isSuccess`, `data`, `error`).
@@ -5,10 +5,8 @@ import { generateKiwtQueryParams } from '../../utils';
5
5
  const useSettingSection = ({
6
6
  sectionName,
7
7
  settingEndpoint,
8
- getQueryNamespaceGenerator = ({ queryParams }) => {
9
- const queryNamespace = ['stripes-kint-components', 'useSetting', 'appSettings', queryParams, sectionName];
10
- return queryNamespace;
11
- },
8
+ getQueryNamespaceGenerator = ({ queryParams }) => ['stripes-kint-components', 'useSetting', 'appSettings', queryParams, sectionName],
9
+ getMutateNamespaceGenerator = () => ['stripes-kint-components', 'useSetting', 'putSetting', sectionName]
12
10
  }) => {
13
11
  const ky = useOkapiKy();
14
12
  const queryParams = generateKiwtQueryParams({
@@ -27,19 +25,21 @@ const useSettingSection = ({
27
25
  stats: false
28
26
  }, {});
29
27
 
30
- const { data: settings = [] } = useQuery(
28
+ const { data: settings = [], ...settingsQuery } = useQuery(
31
29
  getQueryNamespaceGenerator({ queryParams }),
32
30
  () => ky(`${settingEndpoint}?${queryParams?.join('&')}`).json()
33
31
  );
34
32
 
35
- const { mutateAsync: putSetting } = useMutation(
36
- ['stripes-kint-components', 'useSetting', 'putSetting', sectionName],
33
+ const { mutateAsync: putSetting, ...settingsMutate } = useMutation(
34
+ getMutateNamespaceGenerator(),
37
35
  (data) => ky.put(`${settingEndpoint}${data.id ? '/' + data.id : ''}`, { json: data })
38
36
  );
39
37
 
40
38
  return ({
41
39
  handleSubmit: putSetting,
42
- settings
40
+ settings,
41
+ settingsQuery,
42
+ settingsMutate
43
43
  });
44
44
  };
45
45