@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.
Files changed (129) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/LICENSE +1 -1
  3. package/es/index.js +32 -12
  4. package/es/lib/EditableSettingsList/EditableSettingsList.js +4 -3
  5. package/es/lib/EditableSettingsList/EditableSettingsListFieldArray/EditableSettingsListFieldArray.js +110 -0
  6. package/es/lib/EditableSettingsList/{EditableSettingsListFieldArray.test.js → EditableSettingsListFieldArray/EditableSettingsListFieldArray.test.js} +3 -4
  7. package/es/lib/EditableSettingsList/EditableSettingsListFieldArray/index.js +13 -0
  8. package/es/lib/EditableSettingsList/SettingField/{EditSettingValue.js → EditSettingValue/EditSettingValue.js} +2 -2
  9. package/es/lib/EditableSettingsList/SettingField/{EditSettingValue.test.js → EditSettingValue/EditSettingValue.test.js} +2 -3
  10. package/es/lib/EditableSettingsList/SettingField/EditSettingValue/index.js +13 -0
  11. package/es/lib/EditableSettingsList/SettingField/{RenderSettingValue.js → RenderSettingValue/RenderSettingValue.js} +1 -1
  12. package/es/lib/EditableSettingsList/SettingField/{RenderSettingValue.test.js → RenderSettingValue/RenderSettingValue.test.js} +2 -2
  13. package/es/lib/EditableSettingsList/SettingField/RenderSettingValue/index.js +13 -0
  14. package/es/lib/EditableSettingsList/SettingField/SettingField.js +5 -5
  15. package/es/lib/EditableSettingsList/SettingField/SettingField.test.js +54 -44
  16. package/es/lib/EditableSettingsList/index.js +12 -0
  17. package/es/lib/SASQLookupComponent/SASQLookupComponent.js +5 -1
  18. package/es/lib/SearchKeyControl/SearchKeyControl.js +95 -0
  19. package/es/lib/SearchKeyControl/SearchKeyControl.test.js +177 -0
  20. package/es/lib/SearchKeyControl/index.js +13 -0
  21. package/es/lib/SettingPage/SettingPage.js +3 -1
  22. package/es/lib/SettingPage/{SettingPagePane.js → SettingPagePane/SettingPagePane.js} +2 -2
  23. package/es/lib/SettingPage/SettingPagePane/index.js +13 -0
  24. package/es/lib/hooks/__mocks__/index.js +2 -2
  25. package/es/lib/hooks/index.js +33 -21
  26. package/es/lib/hooks/intlHooks/index.js +27 -0
  27. package/es/lib/hooks/intlHooks/useIntlKey/index.js +13 -0
  28. package/es/lib/hooks/{useIntlKey.js → intlHooks/useIntlKey/useIntlKey.js} +1 -1
  29. package/es/lib/hooks/intlHooks/useIntlKeyStore/index.js +13 -0
  30. package/es/lib/hooks/intlHooks/useKintIntl/index.js +13 -0
  31. package/es/lib/hooks/{useKintIntl.js → intlHooks/useKintIntl/useKintIntl.js} +1 -1
  32. package/es/lib/hooks/useInvalidateRefdata/index.js +13 -0
  33. package/es/lib/hooks/{useInvalidateRefdata.js → useInvalidateRefdata/useInvalidateRefdata.js} +1 -1
  34. package/es/lib/hooks/useMutateCustomProperties/index.js +13 -0
  35. package/es/lib/hooks/{useMutateCustomProperties.js → useMutateCustomProperties/useMutateCustomProperties.js} +1 -1
  36. package/es/lib/hooks/useMutateGeneric/index.js +13 -0
  37. package/es/lib/hooks/useMutateRefdataCategory/index.js +13 -0
  38. package/es/lib/hooks/{useMutateRefdataCategory.js → useMutateRefdataCategory/useMutateRefdataCategory.js} +2 -2
  39. package/es/lib/hooks/useMutateRefdataValue/index.js +13 -0
  40. package/es/lib/hooks/{useMutateRefdataValue.js → useMutateRefdataValue/useMutateRefdataValue.js} +2 -2
  41. package/es/lib/settingsHooks/useAppSettings/index.js +13 -0
  42. package/es/lib/settingsHooks/{useAppSettings.js → useAppSettings/useAppSettings.js} +1 -1
  43. package/es/lib/settingsHooks/useSettingSection/index.js +13 -0
  44. package/es/lib/settingsHooks/{useSettingSection.js → useSettingSection/useSettingSection.js} +1 -1
  45. package/es/lib/settingsHooks/useSettings/index.js +13 -0
  46. package/es/lib/settingsHooks/{useSettings.js → useSettings/useSettings.js} +10 -9
  47. package/es/lib/utils/refdataQueryKey/index.js +13 -0
  48. package/package.json +1 -1
  49. package/src/index.js +8 -3
  50. package/src/lib/ActionList/README.md +18 -18
  51. package/src/lib/EditableSettingsList/EditableSettingsList.js +3 -2
  52. package/src/lib/EditableSettingsList/EditableSettingsListFieldArray/EditableSettingsListFieldArray.js +118 -0
  53. package/src/lib/EditableSettingsList/{EditableSettingsListFieldArray.test.js → EditableSettingsListFieldArray/EditableSettingsListFieldArray.test.js} +3 -5
  54. package/src/lib/EditableSettingsList/EditableSettingsListFieldArray/README.md +68 -0
  55. package/src/lib/EditableSettingsList/EditableSettingsListFieldArray/index.js +1 -0
  56. package/src/lib/EditableSettingsList/README.md +74 -0
  57. package/src/lib/EditableSettingsList/SettingField/{EditSettingValue.js → EditSettingValue/EditSettingValue.js} +2 -2
  58. package/src/lib/EditableSettingsList/SettingField/{EditSettingValue.test.js → EditSettingValue/EditSettingValue.test.js} +2 -5
  59. package/src/lib/EditableSettingsList/SettingField/EditSettingValue/README.md +63 -0
  60. package/src/lib/EditableSettingsList/SettingField/EditSettingValue/index.js +1 -0
  61. package/src/lib/EditableSettingsList/SettingField/README.md +61 -0
  62. package/src/lib/EditableSettingsList/SettingField/{RenderSettingValue.js → RenderSettingValue/RenderSettingValue.js} +1 -1
  63. package/src/lib/EditableSettingsList/SettingField/{RenderSettingValue.test.js → RenderSettingValue/RenderSettingValue.test.js} +2 -2
  64. package/src/lib/EditableSettingsList/SettingField/RenderSettingValue/index.js +1 -0
  65. package/src/lib/EditableSettingsList/SettingField/SettingField.js +5 -3
  66. package/src/lib/EditableSettingsList/SettingField/SettingField.test.js +65 -44
  67. package/src/lib/EditableSettingsList/index.js +1 -1
  68. package/src/lib/NumberField/README.md +134 -0
  69. package/src/lib/SASQLookupComponent/README.md +172 -0
  70. package/src/lib/SASQLookupComponent/SASQLookupComponent.js +6 -1
  71. package/src/lib/SASQLookupComponent/TableBody/README.md +113 -0
  72. package/src/lib/SASQRoute/README.md +49 -18
  73. package/src/lib/SASQViewComponent/README.md +132 -0
  74. package/src/lib/SearchKeyControl/README.md +70 -0
  75. package/src/lib/SearchKeyControl/SearchKeyControl.js +98 -0
  76. package/src/lib/SearchKeyControl/SearchKeyControl.test.js +165 -0
  77. package/src/lib/SearchKeyControl/index.js +1 -0
  78. package/src/lib/SettingPage/README.md +66 -0
  79. package/src/lib/SettingPage/SettingPage.js +3 -1
  80. package/src/lib/SettingPage/SettingPagePane/README.md +31 -0
  81. package/src/lib/SettingPage/{SettingPagePane.js → SettingPagePane/SettingPagePane.js} +2 -2
  82. package/src/lib/SettingPage/SettingPagePane/index.js +1 -0
  83. package/src/lib/hooks/README.md +26 -121
  84. package/src/lib/hooks/__mocks__/index.js +2 -2
  85. package/src/lib/hooks/index.js +2 -3
  86. package/src/lib/hooks/intlHooks/README.md +31 -0
  87. package/src/lib/hooks/intlHooks/index.js +3 -0
  88. package/src/lib/hooks/intlHooks/useIntlKey/README.md +23 -0
  89. package/src/lib/hooks/intlHooks/useIntlKey/index.js +1 -0
  90. package/src/lib/hooks/{useIntlKey.js → intlHooks/useIntlKey/useIntlKey.js} +1 -1
  91. package/src/lib/hooks/intlHooks/useIntlKeyStore/README.md +32 -0
  92. package/src/lib/hooks/intlHooks/useIntlKeyStore/index.js +1 -0
  93. package/src/lib/hooks/intlHooks/useKintIntl/README.md +42 -0
  94. package/src/lib/hooks/intlHooks/useKintIntl/index.js +1 -0
  95. package/src/lib/hooks/{useKintIntl.js → intlHooks/useKintIntl/useKintIntl.js} +1 -1
  96. package/src/lib/hooks/useInvalidateRefdata/README.md +72 -0
  97. package/src/lib/hooks/useInvalidateRefdata/index.js +1 -0
  98. package/src/lib/hooks/{useInvalidateRefdata.js → useInvalidateRefdata/useInvalidateRefdata.js} +1 -1
  99. package/src/lib/hooks/useMutateCustomProperties/README.md +88 -0
  100. package/src/lib/hooks/useMutateCustomProperties/index.js +1 -0
  101. package/src/lib/hooks/{useMutateCustomProperties.js → useMutateCustomProperties/useMutateCustomProperties.js} +1 -1
  102. package/src/lib/hooks/useMutateGeneric/README.md +187 -0
  103. package/src/lib/hooks/useMutateGeneric/index.js +1 -0
  104. package/src/lib/hooks/useMutateRefdataCategory/README.md +85 -0
  105. package/src/lib/hooks/useMutateRefdataCategory/index.js +1 -0
  106. package/src/lib/hooks/{useMutateRefdataCategory.js → useMutateRefdataCategory/useMutateRefdataCategory.js} +2 -2
  107. package/src/lib/hooks/useMutateRefdataValue/README.md +154 -0
  108. package/src/lib/hooks/useMutateRefdataValue/index.js +1 -0
  109. package/src/lib/hooks/{useMutateRefdataValue.js → useMutateRefdataValue/useMutateRefdataValue.js} +2 -2
  110. package/src/lib/settingsHooks/useAppSettings/README.md +24 -0
  111. package/src/lib/settingsHooks/useAppSettings/index.js +1 -0
  112. package/src/lib/settingsHooks/{useAppSettings.js → useAppSettings/useAppSettings.js} +1 -1
  113. package/src/lib/settingsHooks/useSettingSection/README.md +54 -0
  114. package/src/lib/settingsHooks/useSettingSection/index.js +1 -0
  115. package/src/lib/settingsHooks/{useSettingSection.js → useSettingSection/useSettingSection.js} +1 -1
  116. package/src/lib/settingsHooks/useSettings/README.md +84 -0
  117. package/src/lib/settingsHooks/useSettings/index.js +1 -0
  118. package/src/lib/settingsHooks/{useSettings.js → useSettings/useSettings.js} +10 -7
  119. package/src/lib/utils/refdataQueryKey/README.md +38 -0
  120. package/src/lib/utils/refdataQueryKey/index.js +1 -0
  121. package/styles/SearchKeyControl.css +14 -0
  122. package/es/lib/EditableSettingsList/EditableSettingsListFieldArray.js +0 -57
  123. package/src/lib/EditableSettingsList/EditableSettingsListFieldArray.js +0 -58
  124. /package/es/lib/hooks/{useIntlKeyStore.js → intlHooks/useIntlKeyStore/useIntlKeyStore.js} +0 -0
  125. /package/es/lib/hooks/{useMutateGeneric.js → useMutateGeneric/useMutateGeneric.js} +0 -0
  126. /package/es/lib/utils/{refdataQueryKey.js → refdataQueryKey/refdataQueryKey.js} +0 -0
  127. /package/src/lib/hooks/{useIntlKeyStore.js → intlHooks/useIntlKeyStore/useIntlKeyStore.js} +0 -0
  128. /package/src/lib/hooks/{useMutateGeneric.js → useMutateGeneric/useMutateGeneric.js} +0 -0
  129. /package/src/lib/utils/{refdataQueryKey.js → refdataQueryKey/refdataQueryKey.js} +0 -0
@@ -0,0 +1,165 @@
1
+ import { useState } from 'react';
2
+
3
+ import { waitFor } from '@folio/jest-config-stripes/testing-library/react';
4
+ import { renderWithIntl, Checkbox } from '@folio/stripes-erm-testing';
5
+
6
+ import SearchKeyControl from './SearchKeyControl';
7
+
8
+ const mockUseQIndex = jest.fn();
9
+
10
+ jest.mock('../hooks', () => ({
11
+ ...jest.requireActual('../hooks'),
12
+ useQIndex: () => mockUseQIndex()
13
+ }));
14
+
15
+ describe('SearchKeyControl', () => {
16
+ describe('without initial qIndex', () => {
17
+ beforeEach(() => {
18
+ mockUseQIndex.mockImplementation(() => {
19
+ return useState();
20
+ });
21
+
22
+ renderWithIntl(
23
+ <SearchKeyControl
24
+ options={[
25
+ {
26
+ label: 'Opt 1',
27
+ key: 'opt1'
28
+ },
29
+ {
30
+ label: 'Opt 2',
31
+ key: 'opt2'
32
+ },
33
+ {
34
+ label: 'Opt 3',
35
+ key: 'opt3'
36
+ }
37
+ ]}
38
+ />
39
+ );
40
+ });
41
+
42
+ test('renders all Checkboxes', async () => {
43
+ await Checkbox('Opt 1').exists();
44
+ await Checkbox('Opt 2').exists();
45
+ await Checkbox('Opt 3').exists();
46
+ });
47
+
48
+ test('all Checkboxes have expected initial "checked" value', async () => {
49
+ await Checkbox('Opt 1').has({ checked: false });
50
+ await Checkbox('Opt 2').has({ checked: false });
51
+ await Checkbox('Opt 3').has({ checked: false });
52
+ });
53
+
54
+ describe('clicking Opt 2', () => {
55
+ beforeEach(async () => {
56
+ await waitFor(async () => {
57
+ await Checkbox('Opt 2').click();
58
+ });
59
+ });
60
+
61
+ test('all Checkboxes have expected "checked" value', async () => {
62
+ await Checkbox('Opt 1').has({ checked: false });
63
+ await Checkbox('Opt 2').has({ checked: true });
64
+ await Checkbox('Opt 3').has({ checked: false });
65
+ });
66
+ });
67
+
68
+ describe('clicking Opt 1 and Opt 2', () => {
69
+ beforeEach(async () => {
70
+ await waitFor(async () => {
71
+ await Checkbox('Opt 1').click();
72
+ await Checkbox('Opt 2').click();
73
+ });
74
+ });
75
+
76
+ test('all Checkboxes have expected "checked" value', async () => {
77
+ await Checkbox('Opt 1').has({ checked: true });
78
+ await Checkbox('Opt 2').has({ checked: true });
79
+ await Checkbox('Opt 3').has({ checked: false });
80
+ });
81
+ });
82
+ });
83
+
84
+ describe('with initial qIndex', () => {
85
+ beforeEach(() => {
86
+ mockUseQIndex.mockImplementation(() => {
87
+ return useState('opt1,opt3');
88
+ });
89
+
90
+ renderWithIntl(
91
+ <SearchKeyControl
92
+ options={[
93
+ {
94
+ label: 'Opt 1',
95
+ key: 'opt1'
96
+ },
97
+ {
98
+ label: 'Opt 2',
99
+ key: 'opt2'
100
+ },
101
+ {
102
+ label: 'Opt 3',
103
+ key: 'opt3'
104
+ }
105
+ ]}
106
+ />
107
+ );
108
+ });
109
+
110
+ test('renders all Checkboxes', async () => {
111
+ await Checkbox('Opt 1').exists();
112
+ await Checkbox('Opt 2').exists();
113
+ await Checkbox('Opt 3').exists();
114
+ });
115
+
116
+ test('all Checkboxes have expected initial "checked" value', async () => {
117
+ await Checkbox('Opt 1').has({ checked: true });
118
+ await Checkbox('Opt 2').has({ checked: false });
119
+ await Checkbox('Opt 3').has({ checked: true });
120
+ });
121
+
122
+ describe('clicking Opt 2', () => {
123
+ beforeEach(async () => {
124
+ await waitFor(async () => {
125
+ await Checkbox('Opt 2').click();
126
+ });
127
+ });
128
+
129
+ test('all Checkboxes have expected "checked" value', async () => {
130
+ await Checkbox('Opt 1').has({ checked: true });
131
+ await Checkbox('Opt 2').has({ checked: true });
132
+ await Checkbox('Opt 3').has({ checked: true });
133
+ });
134
+ });
135
+
136
+ describe('clicking Opt 1 and Opt 2', () => {
137
+ beforeEach(async () => {
138
+ await waitFor(async () => {
139
+ await Checkbox('Opt 1').click();
140
+ await Checkbox('Opt 2').click();
141
+ });
142
+ });
143
+
144
+ test('all Checkboxes have expected "checked" value', async () => {
145
+ await Checkbox('Opt 1').has({ checked: false });
146
+ await Checkbox('Opt 2').has({ checked: true });
147
+ await Checkbox('Opt 3').has({ checked: true });
148
+ });
149
+ });
150
+
151
+ describe('clicking Opt 3', () => {
152
+ beforeEach(async () => {
153
+ await waitFor(async () => {
154
+ await Checkbox('Opt 3').click();
155
+ });
156
+ });
157
+
158
+ test('all Checkboxes have expected "checked" value', async () => {
159
+ await Checkbox('Opt 1').has({ checked: true });
160
+ await Checkbox('Opt 2').has({ checked: false });
161
+ await Checkbox('Opt 3').has({ checked: false });
162
+ });
163
+ });
164
+ });
165
+ });
@@ -0,0 +1 @@
1
+ export { default } from './SearchKeyControl';
@@ -0,0 +1,66 @@
1
+ # SettingPage
2
+
3
+ A container component that simplifies the process of displaying and editing a section of application settings. It leverages the `EditableSettingsList` component to handle the rendering and editing of individual settings within that section. `SettingPage` fetches the relevant settings data for a given `sectionName` using a custom hook and manages the submission of changes.
4
+
5
+ This component acts as a higher-level abstraction, taking care of data fetching and preparing the necessary props for `EditableSettingsList`. It utilizes the `SettingsContext` to access the settings API endpoint and the `useSettingSection` hook to retrieve and handle the settings for a specific section.
6
+
7
+ ## Basic Usage
8
+
9
+ Import the component and provide the `sectionName` to identify the settings section you want to display and manage.
10
+
11
+ ```jsx
12
+ const GeneralSettingsPage = () => {
13
+ return (
14
+ <div>
15
+ <h2>General Application Settings</h2>
16
+ <SettingPage sectionName="general" />
17
+ </div>
18
+ );
19
+ };
20
+ ```
21
+
22
+ In this example, `SettingPage` will fetch the settings associated with the "general" section and render them in an editable list using `EditableSettingsList`.
23
+
24
+ You can also customize the editing capabilities, internationalization keys, and labels.
25
+
26
+ ```jsx
27
+ const AdvancedSettingsPage = () => {
28
+ return (
29
+ <div>
30
+ <h2>Advanced Configuration</h2>
31
+ <SettingPage
32
+ sectionName="advanced"
33
+ allowEdit={false} // Disable editing for this section
34
+ intlKey="advancedSettings"
35
+ intlNS="myApp"
36
+ labelOverrides={{
37
+ 'timeoutDuration': 'Session Timeout (in seconds)'
38
+ }}
39
+ render={({ fields: { name }, index }) => (
40
+ <div key={index}>
41
+ {/* Custom rendering for each setting field */}
42
+ <label htmlFor={`${name}[${index}].value`}>{name}</label>
43
+ <input type="text" id={`${name}[${index}].value`} {...fields.field(`${name}[${index}]`, 'value')} />
44
+ </div>
45
+ )}
46
+ />
47
+ </div>
48
+ );
49
+ };
50
+ ```
51
+
52
+ **Note:**
53
+ * This component relies on the `SettingsContext` being available in the component tree to access the `settingEndpoint`.
54
+ * It also depends on the `useSettingSection` custom hook to handle data fetching and submission logic for the specified `sectionName`.
55
+ * The `render` prop allows for complete customization of how the individual settings are rendered within the `EditableSettingsList`.
56
+
57
+ ## Props
58
+
59
+ | Name | Type | Description | default | required |
60
+ |------------------|-----------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------|----------|
61
+ | `allowEdit` | `boolean` | A flag to globally enable or disable editing for all settings within this section. This prop is passed directly to the underlying `EditableSettingsList` component. | `true` | ✕ |
62
+ | `intlKey` | `string` | A base internationalization key to be used for generating labels within the `EditableSettingsList` and its child components. If provided, it's passed down to `EditableSettingsList`. | `undefined` | ✕ |
63
+ | `intlNS` | `string` | An internationalization namespace used for resolving labels within the `EditableSettingsList` and its child components. If provided, it's passed down to `EditableSettingsList`. | `undefined` | ✕ |
64
+ | `labelOverrides` | `object` | An object containing key-value pairs where the keys are setting identifiers (e.g., `setting.key`) and the values are custom labels to override the default labels generated by `EditableSettingsList`. This prop is passed directly to `EditableSettingsList`. | `{}` | ✕ |
65
+ | `sectionName` | `string` | The unique name of the settings section to be displayed and managed. This prop is crucial as it's used by the `useSettingSection` hook to fetch the relevant settings data from the API. | `undefined` | ✓ |
66
+ | `render` | `func` | A render prop that receives the `fields` object from `react-final-form-arrays` (within `EditableSettingsList`) and allows for complete custom rendering of each setting row. This prop is passed directly to `EditableSettingsList`. See the `EditableSettingsList` documentation for more details on the props passed to this render function and how to use it. | `undefined` | ✕ |
@@ -11,7 +11,8 @@ const SettingPage = ({
11
11
  intlKey: passedIntlKey,
12
12
  intlNS: passedIntlNS,
13
13
  labelOverrides = {},
14
- sectionName
14
+ sectionName,
15
+ render
15
16
  }) => {
16
17
  const { settingEndpoint } = useContext(SettingsContext);
17
18
 
@@ -29,6 +30,7 @@ const SettingPage = ({
29
30
  labelOverrides={labelOverrides}
30
31
  onSave={handleSubmit}
31
32
  onSubmit={handleSubmit}
33
+ render={render}
32
34
  settingSection={sectionName}
33
35
  />
34
36
  );
@@ -0,0 +1,31 @@
1
+ # SettingPagePane
2
+
3
+ A presentational component that wraps its children within a styled pane dedicated to a settings section. **SettingPagePane** is most commonly used to wrap a **SettingsPage** component, which handles the logic for fetching and managing settings data. However, it is not strictly required to wrap a **SettingsPage**—any valid React content can be used as children.
4
+
5
+ This component leverages the `Pane` component from `@folio/stripes/components` to provide a consistent layout and styling for different settings pages. Additionally, it integrates internationalization by using the `useKintIntl` hook along with the `toCamelCase` utility to generate a pane title based on the provided `sectionName`.
6
+
7
+ ## Basic Usage
8
+
9
+ Wrap your settings-related content inside **SettingPagePane**. It is common to use this component as a wrapper for **SettingsPage**; however, you can also use it to contain any other content.
10
+
11
+ ```jsx
12
+ import SettingsPage from './SettingsPage';
13
+
14
+ const GeneralSettings = () => (
15
+ <SettingPagePane sectionName="general">
16
+ {/* In typical usage, SettingsPage is rendered inside SettingPagePane */}
17
+ <SettingsPage sectionName="general" />
18
+ </SettingPagePane>
19
+ );
20
+ ```
21
+
22
+ In this example, **SettingPagePane** creates a pane with an ID of `settings-general` and a title generated by converting `"general"` to camel case and resolving the corresponding internationalized message. If no message is found, it defaults to using `"general"`.
23
+
24
+ ## Props
25
+
26
+ | Name | Type | Description | Default | Required |
27
+ |--------------|--------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------|----------|
28
+ | `sectionName`| `string` | The unique name of the settings section. This prop is used to generate the pane’s identifier and to lookup the internationalized title for the pane header. | `undefined` | ✓ |
29
+ | `intlKey` | `string` | A base internationalization key used by the `useKintIntl` hook to find the correct localized message for the pane title. | `undefined` | ✕ |
30
+ | `intlNS` | `string` | An internationalization namespace for resolving labels within the pane title. | `undefined` | ✕ |
31
+ | `children` | `node` or `func` | The content to be rendered within the pane. Typically, this is a **SettingsPage** component, but it can be any valid React node. | `undefined` | ✕ |
@@ -1,8 +1,8 @@
1
1
  import PropTypes from 'prop-types';
2
2
 
3
3
  import { Pane } from '@folio/stripes/components';
4
- import { toCamelCase } from '../utils';
5
- import { useKintIntl } from '../hooks';
4
+ import { toCamelCase } from '../../utils';
5
+ import { useKintIntl } from '../../hooks';
6
6
 
7
7
  const SettingPagePane = ({
8
8
  children,
@@ -0,0 +1 @@
1
+ export { default } from './SettingPagePane';
@@ -15,85 +15,14 @@ const data = useRefdata({
15
15
  ```
16
16
 
17
17
  ### Props
18
- Name | Type | Description | default | required
19
- --- | --- | --- | --- | ---
20
- endpoint | string | The endpoint to fetch refdataValues from | | ✓ |
21
- desc | string | The refdataCategory (usually of the form `DomainClass.Field`) | | ✕ |
22
- queryParams | object | A set of queryParameters to hand to react-query's `useQuery` | | ✕ |
23
- returnQueryObject | bool | A switch to return the entirety of the queryObject from useQuery. If `false`, the data will be destructured, if `true` the return will be the full object returned by react-query's `useQuery` | false | ✕ |
24
- options | object | An object of the shape SASQ_MAP (See generateKiwtQuery) to pass to the generateKiwtQuery inside. Any passed desc "d" will be passed as a filter `DescKey.${d}`, with DescKey acting as FilterName, assuming `filterKeys: { DescKey: "desc" }` in options, so `desc==${d}` is passed directly to the backend. | `{filterKeys: {DescKey: "desc" }, stats: false, max: 100}` | ✕ |
18
+ | Name | Type | Description | default | required |
19
+ |-------------------|--------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------|----------|
20
+ | endpoint | string | The endpoint to fetch refdataValues from | | ✓ |
21
+ | desc | string | The refdataCategory (usually of the form `DomainClass.Field`) | | ✕ |
22
+ | queryParams | object | A set of queryParameters to hand to react-query's `useQuery` | | ✕ |
23
+ | returnQueryObject | bool | A switch to return the entirety of the queryObject from useQuery. If `false`, the data will be destructured, if `true` the return will be the full object returned by react-query's `useQuery` | false | ✕ |
24
+ | options | object | An object of the shape SASQ_MAP (See generateKiwtQuery) to pass to the generateKiwtQuery inside. Any passed desc "d" will be passed as a filter `DescKey.${d}`, with DescKey acting as FilterName, assuming `filterKeys: { DescKey: "desc" }` in options, so `desc==${d}` is passed directly to the backend. | `{filterKeys: {DescKey: "desc" }, stats: false, max: 100}` | ✕ |
25
25
 
26
- ## useMutateRefdataValue
27
- A hook for mutations (Create/Delete/Edit) of refdataValues.
28
-
29
- ### Basic Usage
30
- ```javascript
31
- import { useMutateRefdataValue } from '@k-int/stripes-kint-components'
32
- ...
33
- const { delete } = useMutateRefdataValue({
34
- endpoint: 'oa/refdata',
35
- id: '1234-abcd-5678',
36
- });
37
- ```
38
-
39
- ### Props
40
- Name | Type | Description | default | required
41
- --- | --- | --- | --- | ---
42
- afterQueryCalls | object | An object of the form `{delete: func1, put: func2}` where `func1`/`func2` are functions to be called after the `delete`/`put`. This is for ease of use, you can alternatively manually use a `.then()` on the functions returned from this hook. | | ✓ |
43
- catchQueryCalls | object | An object of the form `{delete: func1, put: func2}` where `func1`/`func2` are functions to be called with a HTTPError object when the `delete`/`put` calls fail. This is for ease of use, you can alternatively manually use a `.catch()` on the functions returned from this hook. | | ✕ |
44
- endpoint | string | The refdata endpoint | | ✕ |
45
- id | string | The id of the refdata whose values are to be mutated | | ✕ |
46
- queryParams | object | An object of the form `{delete: obj1, put: obj2}`, where `obj1`/`obj2` are set of queryParameters to hand to react-query's `useMutation` for the delete/put operations respectively | | ✕ |
47
- returnQueryObject | object | An object of the form `{delete: bool1, put: bool2}` containing switches to return the entirety of the queryObject from useMutation for `delete` and `put` respectively. If `false`, the mutateAsync function will be destructured, if `true` the return will be the full object returned by react-query's `useMutation` | {put: false, delete: false} | ✕ |
48
-
49
- ### Example
50
- ```javascript
51
- ...
52
- const { delete, put } = useMutateRefdataValue({
53
- afterQueryCalls: {
54
- delete: json => setContentData(json?.values ?? []),
55
- put: json => setContentData(json?.values ?? [])
56
- },
57
- endpoint: 'oa/refdata',
58
- id: refdata?.id,
59
- queryParams: {
60
- delete: {
61
- enabled: !!refdata
62
- },
63
- put: {
64
- enabled: !!refdata
65
- }
66
- }
67
- });
68
- ...
69
- ```
70
-
71
- This block will destructure two `mutateAsync` functions, `delete` and `put`. After a call to either of them the `json` returned will be used in a function call `setContentData`.
72
-
73
- The calls will only be enabled if `refdata` is truthy.
74
-
75
- `put` will return a function `put(data)` and will make a `put` call to add the `data` to the values of the given refdata id. In practice this means calling `put` as follows:
76
- ```javascript
77
- put({
78
- value: 'testValue'
79
- label: 'testLabel'
80
- })
81
- ```
82
- for a new refdataValue, or
83
- ```javascript
84
- put({
85
- id: <id of the refdataValue to edit>
86
- value: 'testValue2'
87
- label: 'testLabel 2'
88
- })
89
- ```
90
- to edit an existing value.
91
-
92
-
93
- `delete` will return a function `delete(data)` and will make a `put` call to glue `_delete: true` to the given value. In practice this means calling `delete` as follows:
94
- ```javascript
95
- delete(<ID of refdata value to remove>)
96
- ```
97
26
 
98
27
  ## useActiveElement
99
28
  A hook which returns the currently focused element in the document, `active`, as well as a function `hasParent`.
@@ -145,9 +74,9 @@ return (
145
74
  ```
146
75
 
147
76
  ### Props
148
- Name | Type | Description | default | required
149
- --- | --- | --- | --- | ---
150
- helpers | object | An object of the form `{helperKey1: Component1, helperKey2: Component1}` where `Component1`/`Component1` are components to be rendered when the url contains query `helper=helperKey1` or `helper=helperKey2` respectively | | ✓ |
77
+ | Name | Type | Description | default | required |
78
+ |---------|--------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------|----------|
79
+ | helpers | object | An object of the form `{helperKey1: Component1, helperKey2: Component1}` where `Component1`/`Component1` are components to be rendered when the url contains query `helper=helperKey1` or `helper=helperKey2` respectively | | ✓ |
151
80
 
152
81
  ## useKiwtSASQuery
153
82
  A hook which sets up a basic queryGetter and querySetter for use with SASQ, as well as setting up the query object itself. Will often be used with the `generateKiwtQuery` function from `utils`.
@@ -285,34 +214,10 @@ export default Test
285
214
  ```
286
215
 
287
216
  ### Props
288
- Name | Type | Description | default | required
289
- --- | --- | --- | --- | ---
290
- name | String | The name of the fieldArray, used to hook into the final form state for that field. | | ✓ |
291
- submitWholeDeletedObject | bool | a boolean flag to ensure that a deleted object is sent whole to the backend, rather than just as an id. | false | ✕ |
292
-
293
- ## useAppSettings
294
- A hook for fetching AppSetting values
295
- ### Basic usage
296
- ```javascript
297
- import { useAppSettings } from '@k-int/stripes-kint-components'
298
- ...
299
- const data = useAppSettings({
300
- endpoint: 'oa/settings/appSettings'
301
- });
302
- ...
303
- ```
304
-
305
- This will return a list of AppSettings. If a `keyName` is passed, this will instead return a single AppSetting object.
306
-
307
- ### Props
308
- Name | Type | Description | default | required
309
- --- | --- | --- | --- | ---
310
- endpoint | string | The endpoint to fetch AppSettings from | | ✓ |
311
- sectionName | string | A string representing a section of AppSettings to filter on | | ✕ |
312
- keyName | string | A string representing an individual key of an AppSettings to filter on. The presence of this prop will change the output shape from Array to Object. It is not strictly necessary to use `keyName` in conjunction with `sectionName`, but keys are not guaranteed to be unique between sections. In addition it may marginally improve performance to use both, even if a key is unique, so it is recommended. | | ✕ |
313
- queryParams | object | A set of queryParameters to hand to react-query's `useQuery` | | ✕ |
314
- returnQueryObject | bool | A switch to return the entirety of the queryObject from useQuery. If `false`, the data will be destructured, if `true` the return will be the full object returned by react-query's `useQuery` | false | ✕ |
315
- options | object | An object of the shape SASQ_MAP (See generateKiwtQuery) to pass to the generateKiwtQuery inside. The default | `{perPage: 100, stats: false, filters: [{path: 'section', value: sectionName }, {path: 'key', value: keyName }]}` | ✕ |
217
+ | Name | Type | Description | default | required |
218
+ |--------------------------|--------|---------------------------------------------------------------------------------------------------------|---------|----------|
219
+ | name | String | The name of the fieldArray, used to hook into the final form state for that field. | | ✓ |
220
+ | submitWholeDeletedObject | bool | a boolean flag to ensure that a deleted object is sent whole to the backend, rather than just as an id. | false | ✕ |
316
221
 
317
222
  ## useMutateGeneric
318
223
 
@@ -336,15 +241,15 @@ updateObject({ id: 456, name: 'Updated Object' }) // Updates the object with id
336
241
 
337
242
  ### Props
338
243
 
339
- | Name | Type | Description | Default | Required |
340
- |---|---|---|--------------------------------------------------------------------------------------------------------------------------------|---|
341
- | afterQueryCalls | object | An object of the form `{ delete: func1, post: func2, put: func3 }` where `func1(responseData)`, `func2(responseData)`, and `func3(responseData)` are functions to be called *after* the corresponding mutation is successful. `responseData` is the data returned by the API call. | `{}` | ✕ |
342
- | catchQueryCalls | object | An object of the form `{ delete: func1, post: func2, put: func3 }` where `func1(error)`, `func2(error)`, and `func3(error)` are functions to be called *if* the corresponding mutation fails. `error` is the HTTPError object. | `{}` | ✕ |
343
- | endpoint | string | The base endpoint for the API calls. | | ✓ |
344
- | endpointMutators | object | An object of the form `{ delete: func1, post: func2, put: func3 }` where `func1(id)`, `func2()`, and `func3(data)` are functions that modify the endpoint for each operation. Defaults to appending the id for `delete` and `put`, and using the base endpoint for `post`. | `{ delete: (id) => "${endpoint}/${id}", post: () => endpoint, put: (data) => "${endpoint}/${data.id}" }` | ✕ |
345
- | payloadMutators | object | An object of the form `{ post: func1, put: func2 }` where `func1(data)` and `func2(data)` are functions that modify the payload for the `post` and `put` operations. Defaults to wrapping the data in a `json` object. | `{ post: (data) => ({ json: data }), put: (data) => ({ json: data }) }` | ✕ |
346
- | promiseReturns | object | An object of the form `{ delete: func1, post: func2, put: func3 }` where `func1(id, ky)`, `func2(data, ky)`, and `func3(data, ky)` are functions that define the promise return for each operation. Allows full control over the `ky` call. `id` is the id for delete, `data` is the data being sent for post/put, and `ky` is the `ky` instance. | `{ delete: (id, ky) => ky.delete(...).json(), post: (data, ky) => ky.post(...).json(), put: (data, ky) => ky.put(...).json() }` | ✕ |
347
- | queryKey | array | The query key to be used with `react-query`. **Must be an array.** | `[]` | ✓ |
348
- | queryKeyMutators | object | An object of the form `{ delete: func1, post: func2, put: func3 }` where `func1()`, `func2()`, and `func3()` are functions that return the query key for each mutation. | `{ delete: () => [...queryKey, 'delete'], post: () => [...queryKey, 'create'], put: () => [...queryKey, 'edit'] }` | ✕ |
349
- | queryParams | object | An object of the form `{ delete: obj1, post: obj2, put: obj3 }` where `obj1`, `obj2`, and `obj3` are the query parameters for each operation. | `{}` | ✕ |
350
- | returnQueryObject | object | An object of the form `{ delete: bool1, post: bool2, put: bool3 }` containing switches to return the entire query object from `useMutation` for each operation. If `false` (the default), the `mutateAsync` function will be returned; if `true`, the full object (including `mutate`, `isLoading`, `error`, etc.) will be returned. | `{ delete: false, post: false, put: false }` | ✕ |
244
+ | Name | Type | Description | Default | Required |
245
+ |-------------------|--------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------|----------|
246
+ | afterQueryCalls | object | An object of the form `{ delete: func1, post: func2, put: func3 }` where `func1(responseData)`, `func2(responseData)`, and `func3(responseData)` are functions to be called *after* the corresponding mutation is successful. `responseData` is the data returned by the API call. | `{}` | ✕ |
247
+ | catchQueryCalls | object | An object of the form `{ delete: func1, post: func2, put: func3 }` where `func1(error)`, `func2(error)`, and `func3(error)` are functions to be called *if* the corresponding mutation fails. `error` is the HTTPError object. | `{}` | ✕ |
248
+ | endpoint | string | The base endpoint for the API calls. | | ✓ |
249
+ | endpointMutators | object | An object of the form `{ delete: func1, post: func2, put: func3 }` where `func1(id)`, `func2()`, and `func3(data)` are functions that modify the endpoint for each operation. Defaults to appending the id for `delete` and `put`, and using the base endpoint for `post`. | `{ delete: (id) => "${endpoint}/${id}", post: () => endpoint, put: (data) => "${endpoint}/${data.id}" }` | ✕ |
250
+ | payloadMutators | object | An object of the form `{ post: func1, put: func2 }` where `func1(data)` and `func2(data)` are functions that modify the payload for the `post` and `put` operations. Defaults to wrapping the data in a `json` object. | `{ post: (data) => ({ json: data }), put: (data) => ({ json: data }) }` | ✕ |
251
+ | promiseReturns | object | An object of the form `{ delete: func1, post: func2, put: func3 }` where `func1(id, ky)`, `func2(data, ky)`, and `func3(data, ky)` are functions that define the promise return for each operation. Allows full control over the `ky` call. `id` is the id for delete, `data` is the data being sent for post/put, and `ky` is the `ky` instance. | `{ delete: (id, ky) => ky.delete(...).json(), post: (data, ky) => ky.post(...).json(), put: (data, ky) => ky.put(...).json() }` | ✕ |
252
+ | queryKey | array | The query key to be used with `react-query`. **Must be an array.** | `[]` | ✓ |
253
+ | queryKeyMutators | object | An object of the form `{ delete: func1, post: func2, put: func3 }` where `func1()`, `func2()`, and `func3()` are functions that return the query key for each mutation. | `{ delete: () => [...queryKey, 'delete'], post: () => [...queryKey, 'create'], put: () => [...queryKey, 'edit'] }` | ✕ |
254
+ | queryParams | object | An object of the form `{ delete: obj1, post: obj2, put: obj3 }` where `obj1`, `obj2`, and `obj3` are the query parameters for each operation. | `{}` | ✕ |
255
+ | returnQueryObject | object | An object of the form `{ delete: bool1, post: bool2, put: bool3 }` containing switches to return the entire query object from `useMutation` for each operation. If `false` (the default), the `mutateAsync` function will be returned; if `true`, the full object (including `mutate`, `isLoading`, `error`, etc.) will be returned. | `{ delete: false, post: false, put: false }` | ✕ |
@@ -1,14 +1,14 @@
1
1
  import refdata from '../../../../test/jest/refdata';
2
2
  import customProperties from '../../../../test/jest/customProperties';
3
3
 
4
- import useKintIntl from '../useKintIntl';
4
+ import useKintIntl from '../intlHooks/useKintIntl';
5
5
 
6
6
  /* EXAMPLE Grab actual hooks for anything not mocked here */
7
7
  const hooks = jest.requireActual('../index');
8
8
 
9
9
  // We have to do this up here too so that our passed useKintIntl
10
10
  // ALSO has a mocked useIntlKeyStore... I think anyway
11
- jest.mock('../useIntlKeyStore', () => () => () => 'ui-test-implementor');
11
+ jest.mock('../intlHooks/useIntlKeyStore', () => () => () => 'ui-test-implementor');
12
12
 
13
13
  module.exports = {
14
14
  ...hooks,
@@ -8,9 +8,6 @@ export { default as useQIndex } from './useQIndex';
8
8
  export { default as useKiwtFieldArray } from './useKiwtFieldArray';
9
9
  export { default as useCustomProperties } from './useCustomProperties';
10
10
  export { default as useInvalidateRefdata } from './useInvalidateRefdata';
11
- export { default as useKintIntl } from './useKintIntl';
12
- export { default as useIntlKeyStore } from './useIntlKeyStore';
13
- export { default as useIntlKey } from './useIntlKey';
14
11
  export { default as useSASQQueryMeta } from './useSASQQueryMeta';
15
12
  export { default as useModConfigEntries } from './useModConfigEntries';
16
13
  export { default as useActionListRef } from './useActionListRef';
@@ -22,3 +19,5 @@ export { default as useMutateCustomProperties } from './useMutateCustomPropertie
22
19
  export { default as useMutateModConfigEntry } from './useMutateModConfigEntry';
23
20
 
24
21
  export { default as usePrevNextPagination } from './usePrevNextPagination';
22
+
23
+ export * from './intlHooks';
@@ -0,0 +1,31 @@
1
+ # Internationalization (intl) Hooks
2
+
3
+ This suite of hooks provides a standardized way to handle internationalization across components while respecting module boundaries. The system allows:
4
+
5
+ 1. **Root Module Configuration** - Set default intl keys/namespaces at application root
6
+ 2. **Component-Level Overrides** - Customize translations per-component via `intlKey`/`intlNS` props
7
+ 3. **Automatic Key Resolution** - Components automatically find translations without hardcoded paths
8
+
9
+ ## Key Components
10
+
11
+ 1. **[useIntlKeyStore](./useIntlKeyStore/README.md)** - Global store for managing intl keys
12
+ 2. **[useIntlKey](./useIntlKey/README.md)** - Hook for resolving intl keys
13
+ 3. **[useKintIntl](./useKintIntl/README.md)** - Enhanced intl formatting hook
14
+
15
+ ## Core Principles
16
+
17
+ - **No Library Translations** - Parent modules provide all translations
18
+ - **Explicit Key Management** - Required key configuration prevents silent failures
19
+ - **Namespace Isolation** - Prevents key collisions between modules
20
+ - **Override Support** - Components can bypass default keys when needed
21
+
22
+ ## Implementation Benefits
23
+
24
+ - ✅ Avoids duplicated translation efforts
25
+ - ✅ Maintains Lokalise compatibility
26
+ - ✅ Enables component reuse across modules
27
+ - ✅ Provides clear error messaging for missing config
28
+ - ✅ Supports both simple and complex i18n scenarios
29
+
30
+ ## Developer note
31
+ This clearly isn't an ideal solution, but it is the best one we could come up with at the time. Each module implementing kint-components can either set up a global key store telling kint-components where to look for their intl keys in general, or set this on a component by component case via the `intlKey` and `intlNS` props used throughout kint-components
@@ -0,0 +1,3 @@
1
+ export { default as useIntlKeyStore } from './useIntlKeyStore';
2
+ export { default as useIntlKey } from './useIntlKey';
3
+ export { default as useKintIntl } from './useKintIntl';
@@ -0,0 +1,23 @@
1
+ # useIntlKey
2
+
3
+ ## Purpose
4
+ Resolves internationalization keys with fallback logic.
5
+
6
+ ## Resolution Order
7
+ 1. Directly passed `intlKey` prop
8
+ 2. Namespace lookup via `intlNS` prop
9
+ 3. Default namespace from `useNamespace`
10
+
11
+ ## Error Handling
12
+ Throws error if:
13
+ - No key passed directly
14
+ - No key found in registry
15
+ - Namespace not registered
16
+
17
+ ## Usage
18
+ ```jsx
19
+ const Component = ({ intlKey, intlNS }) => {
20
+ const resolvedKey = useIntlKey(intlKey, intlNS);
21
+ // Uses resolved key for translations
22
+ };
23
+ ```
@@ -0,0 +1 @@
1
+ export { default } from './useIntlKey';
@@ -1,4 +1,4 @@
1
- import useIntlKeyStore from './useIntlKeyStore';
1
+ import useIntlKeyStore from '../useIntlKeyStore';
2
2
 
3
3
  const useIntlKey = (passedIntlKey, passedIntlNS) => {
4
4
  const getKey = useIntlKeyStore(state => state.getKey);
@@ -0,0 +1,32 @@
1
+ # useIntlKeyStore
2
+
3
+ ## Purpose
4
+ Global state management for internationalization keys using Zustand.
5
+
6
+ ## Key Features
7
+ - Namespace-key pair storage
8
+ - Automatic namespace resolution
9
+ - Hook-safe operations
10
+
11
+ ## Initial Setup
12
+ ```jsx
13
+ // App root component
14
+ import useIntlKeyStore from './useIntlKeyStore';
15
+
16
+ const App = () => {
17
+ const { addKey } = useIntlKeyStore();
18
+
19
+ useEffect(() => {
20
+ addKey('moduleBaseKey', 'myModuleNamespace');
21
+ }, [addKey]);
22
+
23
+ return (...);
24
+ };
25
+ ```
26
+
27
+ ## Store Methods
28
+ | Method | Parameters | Description |
29
+ |--------------|---------------------|--------------------------------------|
30
+ | `addKey` | `(key, namespace)` | Registers namespace-key pair |
31
+ | `removeKey` | `(namespace)` | Removes namespace registration |
32
+ | `getKey` | `(namespace)` | Retrieves key for namespace |
@@ -0,0 +1 @@
1
+ export { default } from './useIntlKeyStore';