@k-int/stripes-kint-components 5.17.0 → 5.18.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 +13 -0
- package/LICENSE +1 -1
- package/es/index.js +24 -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/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/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/package.json +1 -1
- package/src/index.js +4 -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/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/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/es/lib/EditableSettingsList/EditableSettingsListFieldArray.js +0 -57
- package/src/lib/EditableSettingsList/EditableSettingsListFieldArray.js +0 -58
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# SettingField
|
|
2
|
+
|
|
3
|
+
A component designed to render a single setting within a settings list. It handles fetching contextual data (like reference data via `useRefdata` or templates via `useTemplates` based on the setting's configuration), displays the setting's name, help text (`InfoPopover`), and value, and manages the switch between display mode (using `RenderSettingValue`) and edit mode (using `EditSettingValue`).
|
|
4
|
+
|
|
5
|
+
It is typically used as the `component` prop for a `react-final-form` `<Field>` within the structure provided by `EditableSettingsListFieldArray` and `EditableSettingsList`. It utilizes `@folio/stripes/components` (`Card`, `Button`, `InfoPopover`) for its UI structure and relies on `SettingsContext` to determine API endpoints for fetching related data.
|
|
6
|
+
|
|
7
|
+
## Basic Usage
|
|
8
|
+
|
|
9
|
+
`SettingField` is usually rendered internally by the `DefaultField` implementation within `EditableSettingsListField`. It receives props from `react-final-form`'s `<Field>` (like `input`, `meta`) and custom props from `EditableSettingsListField` (like `editing`, `setEditing`, `settingData`, `onSave`).
|
|
10
|
+
|
|
11
|
+
```jsx
|
|
12
|
+
// SettingField is typically used internally by EditableSettingsListField.
|
|
13
|
+
// The default implementation within EditableSettingsListField looks
|
|
14
|
+
// somewhat like this:
|
|
15
|
+
|
|
16
|
+
import { Field } from 'react-final-form';
|
|
17
|
+
|
|
18
|
+
// Inside EditableSettingsListField's render logic (simplified):
|
|
19
|
+
<Field
|
|
20
|
+
// Props passed down:
|
|
21
|
+
allowEdit={props.allowEdit}
|
|
22
|
+
component={SettingField} // <-- Usage of SettingField
|
|
23
|
+
editing={props.editing}
|
|
24
|
+
intlKey={props.intlKey}
|
|
25
|
+
intlNS={props.intlNS}
|
|
26
|
+
labelOverrides={props.labelOverrides}
|
|
27
|
+
onSave={props.onSave} // Specific save handler for this row
|
|
28
|
+
setEditing={props.setEditing} // Function to toggle editing state for this row
|
|
29
|
+
settingData={props.settingData} // Object containing currentSetting etc.
|
|
30
|
+
|
|
31
|
+
// Props provided by Field:
|
|
32
|
+
name={settingIdentifier} // The name/path for this field within the form
|
|
33
|
+
// 'input' and 'meta' props are also automatically passed by Field
|
|
34
|
+
/>
|
|
35
|
+
|
|
36
|
+
// Direct usage is less common but possible if customizing the `render` prop
|
|
37
|
+
// of EditableSettingsListFieldArray or EditableSettingsList.
|
|
38
|
+
|
|
39
|
+
// Note: The actual rendering of the setting's value in view or edit mode
|
|
40
|
+
// is delegated to the RenderSettingValue and EditSettingValue components,
|
|
41
|
+
// respectively, based on the 'editing' prop and the setting's configuration
|
|
42
|
+
// (currentSetting.settingType).
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
**Note:** This component requires `SettingsContext` to be available higher up in the component tree to provide `refdataEndpoint` and `templateEndpoint`.
|
|
46
|
+
|
|
47
|
+
## Props
|
|
48
|
+
|
|
49
|
+
| Name | Type | Description | default | required |
|
|
50
|
+
|------------------------------|-----------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------|------------|
|
|
51
|
+
| `allowEdit` | `boolean` | Controls whether the "Edit"/"Save" button is displayed and if editing is permitted. Passed down from parent components. | `true` | ✕ |
|
|
52
|
+
| `editing` | `boolean` | Determines if the field should render in display mode (`RenderSettingValue`) or edit mode (`EditSettingValue`). This state is typically managed by the parent `EditableSettingsListField`. | `undefined` | ✓ |
|
|
53
|
+
| `input` | `object` | Provided by `react-final-form`'s `<Field>`. Contains field state like `value`, `name`, and callbacks like `onChange`, `onBlur`. Crucial for connecting to the form state, passed down to `EditSettingValue`. | `undefined` | ✓ (by RFF) |
|
|
54
|
+
| `intlKey` | `string` | Base key for internationalization, used by the internal `useKintIntl` hook for fetching labels and messages. | `undefined` | ✕ |
|
|
55
|
+
| `intlNS` | `string` | Namespace for internationalization, used by the internal `useKintIntl` hook. | `undefined` | ✕ |
|
|
56
|
+
| `labelOverrides` | `object` | An object potentially used to override default labels, passed down to `RenderSettingValue`/`EditSettingValue`. | `{}` | ✕ |
|
|
57
|
+
| `meta` | `object` | Provided by `react-final-form`'s `<Field>`. Contains metadata about the field state, such as `touched`, `error`, `dirty`, `valid`, etc. | `undefined` | ✓ (by RFF) |
|
|
58
|
+
| `onSave` | `func` | Callback function triggered when the "Save" button is clicked (while `editing` is true). It should handle the persistence logic for this *single setting* and is expected to return a `Promise`. The success of the promise determines whether the component switches back to display mode (`setEditing(false)`). | `undefined` | ✓ |
|
|
59
|
+
| `setEditing` | `func` | A function, provided by the parent component (e.g., `EditableSettingsListField`), used to signal a change in the editing state (e.g., `setEditing(true)` when "Edit" is clicked, or `setEditing(false)` after a successful save). | `undefined` | ✓ |
|
|
60
|
+
| `settingData` | `object` | An object containing data pertinent to the specific setting being rendered. | `undefined` | ✓ |
|
|
61
|
+
| `settingData.currentSetting` | `object` | The core configuration object for the setting currently being rendered. Contains properties like `key`, `section`, `settingType`, `vocab`, etc., which drive the component's behavior (fetching data, rendering labels, choosing input types via `EditSettingValue`). | `undefined` | ✓ |
|
|
@@ -2,9 +2,9 @@ import React from 'react';
|
|
|
2
2
|
|
|
3
3
|
import RenderSettingValue from './RenderSettingValue';
|
|
4
4
|
|
|
5
|
-
import { renderWithKintHarness } from '
|
|
5
|
+
import { renderWithKintHarness } from '../../../../../test/jest/helpers';
|
|
6
6
|
|
|
7
|
-
jest.mock('
|
|
7
|
+
jest.mock('../../../hooks');
|
|
8
8
|
|
|
9
9
|
const stringSetting = {
|
|
10
10
|
id: '12345',
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './RenderSettingValue';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { useContext } from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
3
|
|
|
4
4
|
import {
|
|
@@ -18,16 +18,17 @@ import renderHelpTextCSS from '../../../../styles/renderHelpText.css';
|
|
|
18
18
|
const SettingField = (settingFieldProps) => {
|
|
19
19
|
const {
|
|
20
20
|
allowEdit = true,
|
|
21
|
+
editing,
|
|
21
22
|
intlKey: passedIntlKey,
|
|
22
23
|
intlNS: passedIntlNS,
|
|
23
24
|
labelOverrides = {},
|
|
24
25
|
onSave,
|
|
26
|
+
setEditing,
|
|
25
27
|
settingData: {
|
|
26
28
|
currentSetting
|
|
27
29
|
} = {}
|
|
28
30
|
} = settingFieldProps;
|
|
29
31
|
|
|
30
|
-
const [editing, setEditing] = useState(false);
|
|
31
32
|
const kintIntl = useKintIntl(passedIntlKey, passedIntlNS);
|
|
32
33
|
const { refdataEndpoint, templateEndpoint } = useContext(SettingsContext);
|
|
33
34
|
|
|
@@ -39,7 +40,8 @@ const SettingField = (settingFieldProps) => {
|
|
|
39
40
|
endpoint: refdataEndpoint,
|
|
40
41
|
desc: currentSetting.vocab,
|
|
41
42
|
queryParams: {
|
|
42
|
-
enabled: !!currentSetting?.vocab && currentSetting.settingType === 'Refdata'
|
|
43
|
+
enabled: !!currentSetting?.vocab && currentSetting.settingType === 'Refdata',
|
|
44
|
+
staleTime: 1000 * 60 * 2 // 2 minutes staletime for refdata in settings?
|
|
43
45
|
}
|
|
44
46
|
});
|
|
45
47
|
|
|
@@ -25,50 +25,71 @@ const setting = {
|
|
|
25
25
|
value: 'diku-shared'
|
|
26
26
|
};
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
let renderComponent;
|
|
30
|
-
beforeEach(async () => {
|
|
31
|
-
renderComponent = renderWithKintHarness(
|
|
32
|
-
<TestForm
|
|
33
|
-
initialValues={{}}
|
|
34
|
-
onSubmit={onSubmit}
|
|
35
|
-
>
|
|
36
|
-
<Field
|
|
37
|
-
component={SettingField}
|
|
38
|
-
name="test"
|
|
39
|
-
onSave={onSave}
|
|
40
|
-
settingData={{
|
|
41
|
-
currentSetting: setting
|
|
42
|
-
}}
|
|
43
|
-
/>
|
|
44
|
-
</TestForm>
|
|
45
|
-
);
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
it('renders RenderSettingValue', async () => {
|
|
49
|
-
const { getByText } = renderComponent;
|
|
50
|
-
await waitFor(async () => expect(await getByText('RenderSettingValue')).toBeInTheDocument());
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
test('renders the edit button', () => {
|
|
54
|
-
Button('Edit').exists();
|
|
55
|
-
});
|
|
28
|
+
const setEditing = jest.fn();
|
|
56
29
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
30
|
+
let renderComponent;
|
|
31
|
+
describe('SettingField', () => {
|
|
32
|
+
describe.each([
|
|
33
|
+
{
|
|
34
|
+
editing: false,
|
|
35
|
+
expectedRender: 'RenderSettingValue',
|
|
36
|
+
expectedButton: 'Edit',
|
|
37
|
+
expectedCallback: setEditing,
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
editing: true,
|
|
41
|
+
expectedRender: 'EditSettingValue',
|
|
42
|
+
expectedButton: 'Save',
|
|
43
|
+
expectedCallback: onSave,
|
|
44
|
+
|
|
45
|
+
}
|
|
46
|
+
])('render SettingField with editing: editing', ({
|
|
47
|
+
editing,
|
|
48
|
+
expectedButton,
|
|
49
|
+
expectedCallback,
|
|
50
|
+
expectedRender
|
|
51
|
+
}) => {
|
|
52
|
+
beforeEach(async () => {
|
|
53
|
+
setEditing.mockClear();
|
|
54
|
+
onSave.mockClear();
|
|
55
|
+
renderComponent = renderWithKintHarness(
|
|
56
|
+
<TestForm
|
|
57
|
+
initialValues={{}}
|
|
58
|
+
onSubmit={onSubmit}
|
|
59
|
+
>
|
|
60
|
+
<Field
|
|
61
|
+
component={SettingField}
|
|
62
|
+
editing={editing}
|
|
63
|
+
name="test"
|
|
64
|
+
onSave={onSave}
|
|
65
|
+
setEditing={setEditing}
|
|
66
|
+
settingData={{
|
|
67
|
+
currentSetting: setting
|
|
68
|
+
}}
|
|
69
|
+
/>
|
|
70
|
+
</TestForm>
|
|
71
|
+
);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it(`renders ${expectedRender}`, () => {
|
|
75
|
+
const { getByText } = renderComponent;
|
|
76
|
+
expect(getByText(expectedRender)).toBeInTheDocument();
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test(`renders the ${expectedButton} button`, () => {
|
|
80
|
+
Button('Edit').exists();
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
describe(`clicking the ${expectedButton} button`, () => {
|
|
84
|
+
beforeEach(async () => {
|
|
85
|
+
await waitFor(async () => {
|
|
86
|
+
await Button(expectedButton).click();
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('fired expected callback', () => {
|
|
91
|
+
expect(expectedCallback).toHaveBeenCalled();
|
|
92
|
+
});
|
|
93
|
+
});
|
|
73
94
|
});
|
|
74
95
|
});
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export { default as EditableSettingsList } from './EditableSettingsList';
|
|
2
2
|
export { default as EditableSettingsListFieldArray } from './EditableSettingsListFieldArray';
|
|
3
|
-
export { SettingField } from './SettingField';
|
|
3
|
+
export { SettingField, RenderSettingValue, EditSettingValue } from './SettingField';
|
|
@@ -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 '
|
|
5
|
-
import { useKintIntl } from '
|
|
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';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './useAppSettings';
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# useSettingSection
|
|
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.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
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.
|
|
12
|
+
|
|
13
|
+
## Basic Usage
|
|
14
|
+
|
|
15
|
+
```jsx
|
|
16
|
+
const SettingsEditor = ({ sectionName, settingEndpoint }) => {
|
|
17
|
+
const { settings, handleSubmit } = useSettingSection({
|
|
18
|
+
sectionName,
|
|
19
|
+
settingEndpoint
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
// Render the settings and handle updates with handleSubmit
|
|
23
|
+
return (
|
|
24
|
+
<div>
|
|
25
|
+
{settings.map(setting => (
|
|
26
|
+
<div key={setting.id}>
|
|
27
|
+
<span>{setting.key}</span>
|
|
28
|
+
{/* Some UI to edit the setting */}
|
|
29
|
+
<button onClick={() => handleSubmit({ ...setting, value: 'new value' })}>
|
|
30
|
+
Save
|
|
31
|
+
</button>
|
|
32
|
+
</div>
|
|
33
|
+
))}
|
|
34
|
+
</div>
|
|
35
|
+
);
|
|
36
|
+
};
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Parameters
|
|
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. | ✓ |
|
|
45
|
+
|
|
46
|
+
## Returns
|
|
47
|
+
|
|
48
|
+
An object containing:
|
|
49
|
+
|
|
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.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './useSettingSection';
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# useSettings
|
|
2
|
+
|
|
3
|
+
A comprehensive hook that manages the retrieval and configuration of settings data for one or multiple sections. It orchestrates multiple queries with `react-query`, constructs dynamic pages for each settings section, and returns a pre-configured `SettingsComponent` for rendering the settings UI.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
`useSettings` serves several purposes:
|
|
8
|
+
|
|
9
|
+
- **Multi-Section Querying:**
|
|
10
|
+
It supports both single and multiple settings sections. Depending on the provided props, it dynamically generates query parameters and uses `useQueries` to fetch data for each section.
|
|
11
|
+
|
|
12
|
+
- **Dynamic Page Generation:**
|
|
13
|
+
When queries return data, it extracts unique section names to create dynamic pages. Each page includes a route, localized label, and a component that wraps a `SettingPage` in a `SettingPagePane`.
|
|
14
|
+
|
|
15
|
+
- **Context and Configuration:**
|
|
16
|
+
The hook creates a `SettingsContextProvider` to pass down configuration details (like API endpoints and internationalization keys) to its children. This ensures that each section or page has the necessary context for rendering.
|
|
17
|
+
|
|
18
|
+
- **Final Settings Component:**
|
|
19
|
+
It returns a `SettingsComponent` that wraps the list of pages or sections using the `Settings` component from `@folio/stripes/smart-components`. This component can then be directly rendered in the application.
|
|
20
|
+
|
|
21
|
+
## Basic Usage
|
|
22
|
+
|
|
23
|
+
```jsx
|
|
24
|
+
const SettingsContainer = () => {
|
|
25
|
+
const {
|
|
26
|
+
isLoading,
|
|
27
|
+
SettingsComponent,
|
|
28
|
+
pageList,
|
|
29
|
+
sections,
|
|
30
|
+
SettingsContextProvider,
|
|
31
|
+
} = useSettings({
|
|
32
|
+
sectionName: 'general',
|
|
33
|
+
settingEndpoint: '/settings',
|
|
34
|
+
refdataEndpoint: '/refdata',
|
|
35
|
+
// Other optional props for customization...
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
if (isLoading) return <div>Loading settings...</div>;
|
|
39
|
+
|
|
40
|
+
return <SettingsComponent />;
|
|
41
|
+
};
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Parameters
|
|
45
|
+
|
|
46
|
+
`useSettings` accepts an optional settings props object (`settingsProps`) that may include the following properties:
|
|
47
|
+
|
|
48
|
+
| Name | Type | Description | Required |
|
|
49
|
+
|------------------------|----------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------|
|
|
50
|
+
| `allowGlobalEdit` | `boolean`| Global flag to enable or disable editing of settings. Defaults to `true`. | ✕ |
|
|
51
|
+
| `dynamicPageExclusions`| `array` | Array of section names to be excluded from dynamic page generation. | ✕ |
|
|
52
|
+
| `intlKey` | `string` | Base internationalization key for resolving labels. | ✕ |
|
|
53
|
+
| `intlNS` | `string` | Internationalization namespace. | ✕ |
|
|
54
|
+
| `labelOverrides` | `object` | Custom label overrides for specific settings keys. | ✕ |
|
|
55
|
+
| `pages` | `array` | If provided, acts as a passthrough configuration for either sections or standalone pages, disabling query execution. | ✕ |
|
|
56
|
+
| `persistentPages` | `array` | Array of pages that persist regardless of dynamic query results. | ✕ |
|
|
57
|
+
| `preventQueries` | `boolean`| Flag to disable querying if settings data is provided externally. | ✕ |
|
|
58
|
+
| `refdataEndpoint` | `string` | The API endpoint to retrieve reference data for settings. | ✕ |
|
|
59
|
+
| `renderSingleSection` | `boolean`| When only one section is available, determines if it should be rendered as a single section with its own label. | ✕ |
|
|
60
|
+
| `sectionPermission` | `string` | Permission string that, if provided, restricts the rendering of a section unless the current user has the specified permission. | ✕ |
|
|
61
|
+
| `sectionRoute` | `string` | Base route path to be used when constructing dynamic page routes for settings sections. | ✕ |
|
|
62
|
+
| `sections` | `array` | Array of section configuration objects. Each object can override or extend the global settings props for that specific section. This means that the shape here matches more or less the shape for the props of `useSettings` itself. | ✕ |
|
|
63
|
+
| `settingEndpoint` | `string` | The base URL endpoint for fetching and updating settings. | ✕ |
|
|
64
|
+
| `templateEndpoint` | `string` | The endpoint for retrieving settings templates (if applicable). | ✕ |
|
|
65
|
+
| `render` | `func` | A custom render function for rendering individual setting fields. This function is passed down to the underlying settings components for specialized display logic. See `EditableSettingsList` for more information on how render is used. | ✕ |
|
|
66
|
+
|
|
67
|
+
## Returns
|
|
68
|
+
|
|
69
|
+
The hook returns an object containing:
|
|
70
|
+
|
|
71
|
+
- **`isLoading`** (`boolean`):
|
|
72
|
+
Indicates if any of the settings queries are still loading.
|
|
73
|
+
|
|
74
|
+
- **`pageList`** (`array`):
|
|
75
|
+
An array of pages to be rendered by the `Settings` component. This is the final list of pages for a single section, if applicable.
|
|
76
|
+
|
|
77
|
+
- **`sections`** (`array`):
|
|
78
|
+
An array of fully configured sections. Each section includes its own `SettingsContextProvider`, dynamic pages, and additional configuration data.
|
|
79
|
+
|
|
80
|
+
- **`SettingsComponent`** (`React Component`):
|
|
81
|
+
A component pre-configured with the pages and sections that renders the settings UI. This component wraps the settings in the required `Settings` component from `@folio/stripes/smart-components`.
|
|
82
|
+
|
|
83
|
+
- **`SettingsContextProvider`** (`React Component`):
|
|
84
|
+
A context provider component for settings configuration. If only one section is available, it is provided directly; otherwise, it defaults to a pass-through provider.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './useSettings';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
3
|
|
|
4
4
|
import { useQueries } from 'react-query';
|
|
@@ -6,11 +6,11 @@ import { useOkapiKy, useStripes } from '@folio/stripes/core';
|
|
|
6
6
|
|
|
7
7
|
import { Settings } from '@folio/stripes/smart-components';
|
|
8
8
|
|
|
9
|
-
import { SettingPage, SettingPagePane } from '
|
|
10
|
-
import { SettingsContext } from '
|
|
9
|
+
import { SettingPage, SettingPagePane } from '../../SettingPage';
|
|
10
|
+
import { SettingsContext } from '../../contexts';
|
|
11
11
|
|
|
12
|
-
import { generateKiwtQueryParams, sortByLabel, toCamelCase } from '
|
|
13
|
-
import { useKintIntl, useIntlKey } from '
|
|
12
|
+
import { generateKiwtQueryParams, sortByLabel, toCamelCase } from '../../utils';
|
|
13
|
+
import { useKintIntl, useIntlKey } from '../../hooks';
|
|
14
14
|
|
|
15
15
|
// TODO work underway to make the settings hook more useful when trying to render multiple sections at a time
|
|
16
16
|
const useSettings = (settingsProps = {}) => {
|
|
@@ -30,7 +30,8 @@ const useSettings = (settingsProps = {}) => {
|
|
|
30
30
|
sections, // if present, should be an array of objects with the SAME shape as the overall props here.
|
|
31
31
|
// If a prop isn't present it can fall back to the "global" prop.
|
|
32
32
|
settingEndpoint,
|
|
33
|
-
templateEndpoint
|
|
33
|
+
templateEndpoint,
|
|
34
|
+
render, // Allow setting-by-setting rendering of fields, to allow for special treatment for a given setting key etc
|
|
34
35
|
} = settingsProps;
|
|
35
36
|
|
|
36
37
|
const ky = useOkapiKy();
|
|
@@ -100,6 +101,7 @@ const useSettings = (settingsProps = {}) => {
|
|
|
100
101
|
const getDynamicPages = useCallback((qra, sma) => {
|
|
101
102
|
const pagesFromQueryReturn = [...new Set(qra.data?.map(s => s.section))];
|
|
102
103
|
const dynamic = pagesFromQueryReturn.map(page => {
|
|
104
|
+
const finalRender = sma.render ?? render;
|
|
103
105
|
const finalSectionRoute = sma.sectionRoute ?? sectionRoute;
|
|
104
106
|
const pageRoute = (finalSectionRoute ? `${finalSectionRoute}/` : '') + page;
|
|
105
107
|
return (
|
|
@@ -120,6 +122,7 @@ const useSettings = (settingsProps = {}) => {
|
|
|
120
122
|
intlKey={passedIntlKey}
|
|
121
123
|
intlNS={passedIntlNS}
|
|
122
124
|
labelOverrides={labelOverrides}
|
|
125
|
+
render={finalRender}
|
|
123
126
|
sectionName={page}
|
|
124
127
|
{...props}
|
|
125
128
|
/>
|
|
@@ -133,7 +136,7 @@ const useSettings = (settingsProps = {}) => {
|
|
|
133
136
|
pages: pagesFromQueryReturn,
|
|
134
137
|
dynamic,
|
|
135
138
|
};
|
|
136
|
-
}, [allowGlobalEdit, kintIntl, labelOverrides, passedIntlKey, passedIntlNS, sectionRoute]);
|
|
139
|
+
}, [allowGlobalEdit, kintIntl, labelOverrides, passedIntlKey, passedIntlNS, render, sectionRoute]);
|
|
137
140
|
|
|
138
141
|
useEffect(() => {
|
|
139
142
|
// Handle loading
|