@k-int/stripes-kint-components 5.16.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 +20 -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/SASQLookupComponent/SASQLookupComponent.js +26 -9
- package/es/lib/SASQViewComponent/SASQViewComponent.js +21 -7
- 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/SASQLookupComponent/SASQLookupComponent.js +21 -10
- package/src/lib/SASQViewComponent/SASQViewComponent.js +17 -8
- 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,63 @@
|
|
|
1
|
+
# EditSettingValue
|
|
2
|
+
|
|
3
|
+
A component responsible for rendering the correct input field when a setting is in edit mode within the `SettingField` component. It selects and configures the appropriate `react-final-form` `<Field>`-wrapped input component (`@folio/stripes/components` `TextField`, `Select`, or a custom `RefdataButtons` component) based on the `settingType` specified in the setting's configuration (`currentSetting` prop).
|
|
4
|
+
|
|
5
|
+
This component is designed to be rendered by `SettingField` when the `editing` prop is true. It receives form control props (`input`) from the parent `react-final-form` `<Field>` and contextual data (`refdata`, `templates`) needed for specific input types like dropdowns or radio buttons. It ensures that the edited value is correctly bound to the `.value` property of the setting object within the form state.
|
|
6
|
+
|
|
7
|
+
## Basic Usage
|
|
8
|
+
|
|
9
|
+
`EditSettingValue` is used internally by the `SettingField` component when its `editing` prop is true. It's not typically instantiated directly in application code but rather chosen dynamically by `SettingField`.
|
|
10
|
+
|
|
11
|
+
```jsx
|
|
12
|
+
// EditSettingValue is used internally by the SettingField component
|
|
13
|
+
// when its 'editing' prop is true.
|
|
14
|
+
|
|
15
|
+
// Simplified structure within SettingField's render method:
|
|
16
|
+
import EditSettingValue from './EditSettingValue'; // Adjust path
|
|
17
|
+
import RenderSettingValue from './RenderSettingValue'; // For comparison
|
|
18
|
+
|
|
19
|
+
const SettingField = ({ editing, input, meta, currentSetting, refdata, templates, ...rest }) => {
|
|
20
|
+
// ... other logic like fetching refdata/templates ...
|
|
21
|
+
|
|
22
|
+
let RenderFunction;
|
|
23
|
+
if (editing === false) {
|
|
24
|
+
// When not editing, SettingField uses RenderSettingValue
|
|
25
|
+
RenderFunction = RenderSettingValue;
|
|
26
|
+
} else {
|
|
27
|
+
// When editing is true, SettingField switches to EditSettingValue:
|
|
28
|
+
RenderFunction = EditSettingValue;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<Card /* Card setup */ >
|
|
33
|
+
{/* RenderFunction will be EditSettingValue when editing is true */}
|
|
34
|
+
<RenderFunction
|
|
35
|
+
currentSetting={currentSetting}
|
|
36
|
+
input={input} // Passed from the parent <Field>
|
|
37
|
+
meta={meta} // Also passed from the parent <Field>
|
|
38
|
+
refdata={refdata} // Fetched refdata if settingType is 'Refdata'
|
|
39
|
+
templates={templates} // Fetched templates if settingType is 'Template'
|
|
40
|
+
{...rest} // Other relevant props like intlKey, intlNS, labelOverrides etc.
|
|
41
|
+
/>
|
|
42
|
+
</Card>
|
|
43
|
+
);
|
|
44
|
+
};
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
The specific input rendered depends on `currentSetting.settingType`:
|
|
48
|
+
* **'Refdata'**: Renders `<Select>` or `<RefdataButtons>` based on the number of options in the `refdata` prop.
|
|
49
|
+
* **'Password'**: Renders `<TextField type="password">`.
|
|
50
|
+
* **'Template'**: Renders `<Select>` populated from the `templates` prop.
|
|
51
|
+
* **Default/Other**: Renders a standard `<TextField>`.
|
|
52
|
+
|
|
53
|
+
## Props
|
|
54
|
+
|
|
55
|
+
| Name | Type | Description | default | required |
|
|
56
|
+
|------------------|----------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------|-------------------------------------|
|
|
57
|
+
| `currentSetting` | `object` | The configuration object for the setting being edited. Its `settingType` property dictates which input component is rendered, and `key` is used for generating labels. | `undefined` | ✓ |
|
|
58
|
+
| `input` | `object` | Provided by the parent `react-final-form` `<Field>`. Contains the field's `name`, `value`, `onChange`, `onBlur`, etc., connecting the rendered input to the form state. The component uses `input.name` to construct the name for the nested field (e.g., `parentName.value`). | `undefined` | ✓ |
|
|
59
|
+
| `intlKey` | `string` | Base key for internationalization passed to the `useKintIntl` hook, used for generating the `aria-label`. | `undefined` | ✕ |
|
|
60
|
+
| `intlNS` | `string` | Namespace for internationalization passed to the `useKintIntl` hook, used for generating the `aria-label`. | `undefined` | ✕ |
|
|
61
|
+
| `labelOverrides` | `object` | An object potentially containing overrides for specific internationalization message IDs used in this component (e.g., `valueFor`). | `{}` | ✕ |
|
|
62
|
+
| `refdata` | `array` | An array of reference data objects (typically `{ id, value, label }`). Expected to be provided if `currentSetting.settingType` is 'Refdata'. Used to populate the `Select` or `RefdataButtons` component. | `undefined` | Conditional (✓ for 'Refdata' type) |
|
|
63
|
+
| `templates` | `array` | An array of template objects (expected shape `{ id, name }`). Expected to be provided if `currentSetting.settingType` is 'Template'. Used to populate the `Select` component. | `undefined` | Conditional (✓ for 'Template' type) |
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './EditSettingValue';
|
|
@@ -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';
|
|
@@ -40,6 +40,17 @@ const SASQLookupComponent = forwardRef((props, ref) => {
|
|
|
40
40
|
intlKey: passedIntlKey,
|
|
41
41
|
intlNS: passedIntlNS,
|
|
42
42
|
labelOverrides = {},
|
|
43
|
+
lookupQueryNamespaceGenerator = ({ currentPage, namespace, id: passedId, query }) => {
|
|
44
|
+
const queryNamespace = [namespace, 'SASQ'];
|
|
45
|
+
if (passedId) {
|
|
46
|
+
queryNamespace.push(passedId);
|
|
47
|
+
}
|
|
48
|
+
queryNamespace.push('viewAll');
|
|
49
|
+
queryNamespace.push(query);
|
|
50
|
+
queryNamespace.push(currentPage);
|
|
51
|
+
|
|
52
|
+
return queryNamespace;
|
|
53
|
+
},
|
|
43
54
|
mainPaneProps = {},
|
|
44
55
|
mclProps = {},
|
|
45
56
|
noSearchField,
|
|
@@ -49,7 +60,7 @@ const SASQLookupComponent = forwardRef((props, ref) => {
|
|
|
49
60
|
rowNavigation = true, // Default navigation onRowClick
|
|
50
61
|
sasqProps,
|
|
51
62
|
searchFieldAriaLabel,
|
|
52
|
-
searchFieldProps
|
|
63
|
+
searchFieldProps,
|
|
53
64
|
} = props;
|
|
54
65
|
const kintIntl = useKintIntl(passedIntlKey, passedIntlNS);
|
|
55
66
|
const [count, setCount] = useState(0);
|
|
@@ -95,24 +106,24 @@ const SASQLookupComponent = forwardRef((props, ref) => {
|
|
|
95
106
|
const [filterPaneVisible, setFilterPaneVisible] = useLocalStorageState(filterPaneVisibileKey, true);
|
|
96
107
|
const toggleFilterPane = () => setFilterPaneVisible(!filterPaneVisible);
|
|
97
108
|
|
|
98
|
-
const queryNamespace = [namespace, 'SASQ'];
|
|
99
|
-
if (id) {
|
|
100
|
-
queryNamespace.push(id);
|
|
101
|
-
}
|
|
102
|
-
queryNamespace.push('viewAll');
|
|
103
|
-
queryNamespace.push(query);
|
|
104
|
-
queryNamespace.push(currentPage);
|
|
105
|
-
|
|
106
109
|
const {
|
|
107
110
|
data = {},
|
|
108
111
|
...restOfQueryProps
|
|
109
112
|
} = useQuery(
|
|
110
|
-
|
|
113
|
+
lookupQueryNamespaceGenerator({
|
|
114
|
+
currentPage,
|
|
115
|
+
endpoint: fetchParameters.endpoint,
|
|
116
|
+
namespace,
|
|
117
|
+
id,
|
|
118
|
+
query,
|
|
119
|
+
queryParams
|
|
120
|
+
}),
|
|
111
121
|
() => {
|
|
112
122
|
return ky.get(`${fetchParameters.endpoint}${queryParams}`).json();
|
|
113
123
|
},
|
|
114
124
|
{
|
|
115
125
|
enabled: (!!query?.filters || !!query?.query) && !!currentPage,
|
|
126
|
+
...(fetchParameters.queryOptions ?? {})
|
|
116
127
|
}
|
|
117
128
|
);
|
|
118
129
|
|
|
@@ -16,6 +16,16 @@ const SASQViewComponent = forwardRef(({
|
|
|
16
16
|
match,
|
|
17
17
|
path,
|
|
18
18
|
ViewComponent,
|
|
19
|
+
viewQueryNamespaceGenerator = ({ namespace, componentId, id: passedId }) => {
|
|
20
|
+
const queryNamespace = [namespace, 'SASQ'];
|
|
21
|
+
if (componentId) {
|
|
22
|
+
queryNamespace.push(componentId);
|
|
23
|
+
}
|
|
24
|
+
queryNamespace.push('view');
|
|
25
|
+
queryNamespace.push(passedId);
|
|
26
|
+
|
|
27
|
+
return queryNamespace;
|
|
28
|
+
},
|
|
19
29
|
...props
|
|
20
30
|
}, ref) => {
|
|
21
31
|
const { 0: namespace } = useNamespace();
|
|
@@ -23,16 +33,15 @@ const SASQViewComponent = forwardRef(({
|
|
|
23
33
|
// If itemEndpoint is available, use that, otherwise use standard endpoint
|
|
24
34
|
const endpoint = fetchParameters?.itemEndpoint ?? fetchParameters?.endpoint;
|
|
25
35
|
|
|
26
|
-
const queryNamespace = [namespace, 'SASQ'];
|
|
27
|
-
if (id) {
|
|
28
|
-
queryNamespace.push(id);
|
|
29
|
-
}
|
|
30
|
-
queryNamespace.push('view');
|
|
31
|
-
queryNamespace.push(match?.params?.id);
|
|
32
|
-
|
|
33
36
|
const ky = useOkapiKy();
|
|
34
37
|
const { data = {}, ...rest } = useQuery(
|
|
35
|
-
|
|
38
|
+
viewQueryNamespaceGenerator({
|
|
39
|
+
componentId: id,
|
|
40
|
+
namespace,
|
|
41
|
+
endpoint,
|
|
42
|
+
id: match?.params?.id,
|
|
43
|
+
match,
|
|
44
|
+
}),
|
|
36
45
|
() => ky(`${endpoint}/${match?.params?.id}`).json(),
|
|
37
46
|
{
|
|
38
47
|
enabled: !!match?.params?.id
|
|
@@ -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';
|