@k-int/stripes-kint-components 5.18.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 (73) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/es/index.js +8 -0
  3. package/es/lib/SASQLookupComponent/SASQLookupComponent.js +5 -1
  4. package/es/lib/SearchKeyControl/SearchKeyControl.js +95 -0
  5. package/es/lib/SearchKeyControl/SearchKeyControl.test.js +177 -0
  6. package/es/lib/SearchKeyControl/index.js +13 -0
  7. package/es/lib/hooks/__mocks__/index.js +2 -2
  8. package/es/lib/hooks/index.js +33 -21
  9. package/es/lib/hooks/intlHooks/index.js +27 -0
  10. package/es/lib/hooks/intlHooks/useIntlKey/index.js +13 -0
  11. package/es/lib/hooks/{useIntlKey.js → intlHooks/useIntlKey/useIntlKey.js} +1 -1
  12. package/es/lib/hooks/intlHooks/useIntlKeyStore/index.js +13 -0
  13. package/es/lib/hooks/intlHooks/useKintIntl/index.js +13 -0
  14. package/es/lib/hooks/{useKintIntl.js → intlHooks/useKintIntl/useKintIntl.js} +1 -1
  15. package/es/lib/hooks/useInvalidateRefdata/index.js +13 -0
  16. package/es/lib/hooks/{useInvalidateRefdata.js → useInvalidateRefdata/useInvalidateRefdata.js} +1 -1
  17. package/es/lib/hooks/useMutateCustomProperties/index.js +13 -0
  18. package/es/lib/hooks/{useMutateCustomProperties.js → useMutateCustomProperties/useMutateCustomProperties.js} +1 -1
  19. package/es/lib/hooks/useMutateGeneric/index.js +13 -0
  20. package/es/lib/hooks/useMutateRefdataCategory/index.js +13 -0
  21. package/es/lib/hooks/{useMutateRefdataCategory.js → useMutateRefdataCategory/useMutateRefdataCategory.js} +2 -2
  22. package/es/lib/hooks/useMutateRefdataValue/index.js +13 -0
  23. package/es/lib/hooks/{useMutateRefdataValue.js → useMutateRefdataValue/useMutateRefdataValue.js} +2 -2
  24. package/es/lib/utils/refdataQueryKey/index.js +13 -0
  25. package/package.json +1 -1
  26. package/src/index.js +4 -0
  27. package/src/lib/NumberField/README.md +134 -0
  28. package/src/lib/SASQLookupComponent/README.md +172 -0
  29. package/src/lib/SASQLookupComponent/SASQLookupComponent.js +6 -1
  30. package/src/lib/SASQLookupComponent/TableBody/README.md +113 -0
  31. package/src/lib/SASQRoute/README.md +49 -18
  32. package/src/lib/SASQViewComponent/README.md +132 -0
  33. package/src/lib/SearchKeyControl/README.md +70 -0
  34. package/src/lib/SearchKeyControl/SearchKeyControl.js +98 -0
  35. package/src/lib/SearchKeyControl/SearchKeyControl.test.js +165 -0
  36. package/src/lib/SearchKeyControl/index.js +1 -0
  37. package/src/lib/hooks/README.md +26 -121
  38. package/src/lib/hooks/__mocks__/index.js +2 -2
  39. package/src/lib/hooks/index.js +2 -3
  40. package/src/lib/hooks/intlHooks/README.md +31 -0
  41. package/src/lib/hooks/intlHooks/index.js +3 -0
  42. package/src/lib/hooks/intlHooks/useIntlKey/README.md +23 -0
  43. package/src/lib/hooks/intlHooks/useIntlKey/index.js +1 -0
  44. package/src/lib/hooks/{useIntlKey.js → intlHooks/useIntlKey/useIntlKey.js} +1 -1
  45. package/src/lib/hooks/intlHooks/useIntlKeyStore/README.md +32 -0
  46. package/src/lib/hooks/intlHooks/useIntlKeyStore/index.js +1 -0
  47. package/src/lib/hooks/intlHooks/useKintIntl/README.md +42 -0
  48. package/src/lib/hooks/intlHooks/useKintIntl/index.js +1 -0
  49. package/src/lib/hooks/{useKintIntl.js → intlHooks/useKintIntl/useKintIntl.js} +1 -1
  50. package/src/lib/hooks/useInvalidateRefdata/README.md +72 -0
  51. package/src/lib/hooks/useInvalidateRefdata/index.js +1 -0
  52. package/src/lib/hooks/{useInvalidateRefdata.js → useInvalidateRefdata/useInvalidateRefdata.js} +1 -1
  53. package/src/lib/hooks/useMutateCustomProperties/README.md +88 -0
  54. package/src/lib/hooks/useMutateCustomProperties/index.js +1 -0
  55. package/src/lib/hooks/{useMutateCustomProperties.js → useMutateCustomProperties/useMutateCustomProperties.js} +1 -1
  56. package/src/lib/hooks/useMutateGeneric/README.md +187 -0
  57. package/src/lib/hooks/useMutateGeneric/index.js +1 -0
  58. package/src/lib/hooks/useMutateRefdataCategory/README.md +85 -0
  59. package/src/lib/hooks/useMutateRefdataCategory/index.js +1 -0
  60. package/src/lib/hooks/{useMutateRefdataCategory.js → useMutateRefdataCategory/useMutateRefdataCategory.js} +2 -2
  61. package/src/lib/hooks/useMutateRefdataValue/README.md +154 -0
  62. package/src/lib/hooks/useMutateRefdataValue/index.js +1 -0
  63. package/src/lib/hooks/{useMutateRefdataValue.js → useMutateRefdataValue/useMutateRefdataValue.js} +2 -2
  64. package/src/lib/settingsHooks/useAppSettings/README.md +24 -0
  65. package/src/lib/utils/refdataQueryKey/README.md +38 -0
  66. package/src/lib/utils/refdataQueryKey/index.js +1 -0
  67. package/styles/SearchKeyControl.css +14 -0
  68. /package/es/lib/hooks/{useIntlKeyStore.js → intlHooks/useIntlKeyStore/useIntlKeyStore.js} +0 -0
  69. /package/es/lib/hooks/{useMutateGeneric.js → useMutateGeneric/useMutateGeneric.js} +0 -0
  70. /package/es/lib/utils/{refdataQueryKey.js → refdataQueryKey/refdataQueryKey.js} +0 -0
  71. /package/src/lib/hooks/{useIntlKeyStore.js → intlHooks/useIntlKeyStore/useIntlKeyStore.js} +0 -0
  72. /package/src/lib/hooks/{useMutateGeneric.js → useMutateGeneric/useMutateGeneric.js} +0 -0
  73. /package/src/lib/utils/{refdataQueryKey.js → refdataQueryKey/refdataQueryKey.js} +0 -0
@@ -0,0 +1,132 @@
1
+ # SASQViewComponent
2
+
3
+ A component that fetches and displays detailed data for a single item within a 3-pane layout. **SASQViewComponent** is typically used as the view pane in conjunction with **SASQLookupComponent** (for list views) via **SASQRoute**. It handles data fetching for individual records and renders content through a customizable `ViewComponent`.
4
+
5
+ ## Basic Usage
6
+
7
+ **SASQViewComponent** is generally used indirectly through **SASQRoute**. Below is a minimal example showing direct usage (though typically configured via **SASQRoute**):
8
+
9
+ ```jsx
10
+ import { SASQViewComponent } from '@k-int/stripes-kint-components';
11
+ import ItemDetailView from './ItemDetailView';
12
+
13
+ // In a route configuration (e.g., within a <Route> component)
14
+ const ViewPane = (routeProps) => (
15
+ <SASQViewComponent
16
+ fetchParameters={{
17
+ endpoint: '/api/items', // Default endpoint for individual items
18
+ itemEndpoint: '/api/items/details' // Optional: override for item-specific endpoint
19
+ }}
20
+ ViewComponent={ItemDetailView}
21
+ {...routeProps}
22
+ />
23
+ );
24
+ ```
25
+
26
+ In this example, when navigating to a URL like `/items/123`, **SASQViewComponent**:
27
+ 1. Fetches data from `/api/items/details/123` (using `itemEndpoint`)
28
+ 2. Renders `ItemDetailView` with the fetched data
29
+ 3. Provides a default close button to return to the list view
30
+
31
+ ## Props
32
+
33
+ | Prop | Type | Required | Description |
34
+ |-----------------------------|--------------------------------------------------|----------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
35
+ | `fetchParameters` | `object` | ✓ | Configuration for data fetching. Requires either:<br>• `endpoint`: Base endpoint (appended with `/{id}`)<br>• `itemEndpoint`: Specific endpoint for items (override) |
36
+ | `ViewComponent` | `Component` | ✓ | Component to render the item details. Receives `resource` (data), `onClose` (close handler), and `queryProps` (loading/error states). |
37
+ | `viewQueryNamespaceGenerator` | `function` | ✕ | Generates a unique query key for react-query. Default: Creates a namespace based on component ID and route params. |
38
+ | `id` | `string` | ✕ | Unique identifier for the component. Used in query key generation. |
39
+ | `path` | `string` | ✓ | Base path for navigation (e.g., `/items`). Used with `history` to return to list view. |
40
+ | `history` | `object` | ✓ | React Router history object (injected by parent route). |
41
+ | `location` | `object` | ✓ | React Router location object (injected by parent route). |
42
+ | `match` | `object` | ✓ | React Router match object containing route params (e.g., `match.params.id`). |
43
+
44
+ ## Key Features
45
+
46
+ 1. **Automatic Data Fetching**
47
+ Fetches individual item data when `match.params.id` exists. Uses either:
48
+ ```js
49
+ `${itemEndpoint}/${id}` // If itemEndpoint provided
50
+ `${endpoint}/${id}` // If only endpoint provided
51
+ ```
52
+
53
+ 2. **Query Management**
54
+ Uses `react-query` for fetching, caching, and error handling. Exposes query states (`isLoading`, `isError`, etc.) via `queryProps` passed to `ViewComponent`.
55
+
56
+ 3. **Close Behavior**
57
+ Provides a default `onClose` handler that navigates back to the parent path (e.g., from `/items/123` to `/items`).
58
+
59
+ 4. **Customizable Query Keys**
60
+ Override `viewQueryNamespaceGenerator` to control react-query's cache key structure:
61
+ ```jsx
62
+ <SASQViewComponent
63
+ viewQueryNamespaceGenerator={({ namespace, id }) => [
64
+ namespace,
65
+ 'custom-view',
66
+ id
67
+ ]}
68
+ />
69
+ ```
70
+
71
+ 5. **Ref Forwarding**
72
+ Exposes query data and status via ref:
73
+ ```jsx
74
+ const viewRef = useRef();
75
+ // Access { data, isLoading, ... } via viewRef.current.queryProps
76
+ ```
77
+
78
+ ## Integration with ViewComponent
79
+
80
+ Your `ViewComponent` receives these props:
81
+
82
+ | Prop | Type | Description |
83
+ |--------------|-----------|-------------------------------------------------------------------------------------------------|
84
+ | `resource` | `object` | Fetched data for the item. |
85
+ | `onClose` | `function`| Callback to close the view pane (returns to list). |
86
+ | `queryProps` | `object` | Contains `isLoading`, `isError`, `error`, and other react-query states. |
87
+
88
+ Example `ViewComponent` implementation:
89
+ ```jsx
90
+ const ItemDetailView = ({ resource, onClose, queryProps }) => {
91
+ if (queryProps.isLoading) return <Spinner />;
92
+ if (queryProps.isError) return <Error message={queryProps.error.message} />;
93
+
94
+ return (
95
+ <div>
96
+ <button onClick={onClose}>Back</button>
97
+ <h1>{resource.name}</h1>
98
+ {/* Render other details */}
99
+ </div>
100
+ );
101
+ };
102
+ ```
103
+
104
+ ## Usage with SASQRoute
105
+
106
+ Typically used via **SASQRoute** for automatic route configuration:
107
+ ```jsx
108
+ <SASQRoute
109
+ path="/items"
110
+ fetchParameters={{
111
+ endpoint: '/api/items',
112
+ SASQ_MAP: { searchKey: 'name' }
113
+ }}
114
+ ViewComponent={ItemDetailView}
115
+ resultColumns={[...]}
116
+ />
117
+ ```
118
+ This automatically:
119
+ - Creates routes for `/items` (list) and `/items/:id` (detail)
120
+ - Connects the lookup and view components
121
+ - Passes fetched data to `ItemDetailView`
122
+
123
+ ## Edge Cases
124
+
125
+ - **Missing ID Parameter:**
126
+ If `match.params.id` is undefined, no data fetching occurs and `resource` will be `undefined`.
127
+
128
+ - **Custom Endpoints:**
129
+ Use `itemEndpoint` when individual items require a different API path than `{endpoint}/{id}`.
130
+
131
+ - **Query Key Collisions:**
132
+ Override `viewQueryNamespaceGenerator` if multiple SASQViewComponents share the same ID/route.
@@ -0,0 +1,70 @@
1
+ # SearchKeyControl
2
+
3
+ A component that renders a group of checkboxes allowing users to select one or more search keys (indexes). It directly interacts with the `qIndex` URL parameter (managed via the `useQIndex` hook) to reflect the currently active search indexes.
4
+
5
+ > **Note:** This component is designed to be controlled by the `qIndex` URL parameter rather than receiving explicit `value` or `onChange` props for the entire set of keys. It modifies the `qIndex` parameter by adding or removing individual keys based on checkbox interactions.
6
+
7
+ ## Basic Usage
8
+
9
+ Below is an example demonstrating how to use **SearchKeyControl**. You provide an array of `options`, each defining a potential search key with a label. The component automatically reads the current `qIndex` from the URL, determines the checked state of each checkbox, and updates the `qIndex` when checkboxes are toggled.
10
+
11
+ ```jsx
12
+ import { SearchKeyControl } from '@k-int/stripes-kint-components';
13
+
14
+ const MySearchForm = () => {
15
+ const searchKeyOptions = [
16
+ { key: 'keyword', label: 'Keyword' },
17
+ { key: 'title', label: 'Title' },
18
+ { key: 'author', label: 'Author' },
19
+ { key: 'subject', label: 'Subject' }
20
+ ];
21
+
22
+ return (
23
+ <div>
24
+ {/* ... other search form elements ... */}
25
+ <label>Search Indexes:</label>
26
+ <SearchKeyControl options={searchKeyOptions} />
27
+ {/* ... submit button etc ... */}
28
+ </div>
29
+ );
30
+ };
31
+
32
+ export default MySearchForm;
33
+ ````
34
+
35
+ In this example, **SearchKeyControl**:
36
+
37
+ - Renders checkboxes for "Keyword", "Title", "Author", and "Subject".
38
+ - Reads the `qIndex` URL parameter (e.g., `?qIndex=keyword,title`).
39
+ - Sets the "Keyword" and "Title" checkboxes as checked based on the example `qIndex`.
40
+ - If the user clicks the "Author" checkbox, the component updates the URL parameter to `?qIndex=keyword,title,author`.
41
+ - If the user then unclicks the "Keyword" checkbox, the URL parameter becomes `?qIndex=title,author`.
42
+ - It also automatically resets pagination (via `usePrevNextPagination`) when the `qIndex` changes.
43
+
44
+ ## Props
45
+
46
+ | Name | Type | Description | Default | Required |
47
+ |:----------|:------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------|:---------|
48
+ | `options` | `arrayOf(object)` | An array of objects defining the available search keys. Each object must have: <br>• `key` (string): The value representing the search index. <br/>• `label` (string \| node): The text or element displayed next to the checkbox. | `[]` | ✕ |
49
+
50
+
51
+ ## How It Works
52
+
53
+ 1. **Reading Search Index State:**
54
+ The component uses the `useQIndex` hook to read the current `qIndex` URL parameter, which is expected to be a comma-separated string of active search keys (e.g., `"keyword,title"`). It memoizes this string split into an array (`qIndexArray`).
55
+
56
+ 2. **Determining Checkbox State:**
57
+ It uses `useState`, `useEffect`, `useMemo`, and `useCallback` along with `lodash/isEqual` to create and maintain an internal `keyState` object. This object maps each `key` from the `options` prop to its display `label` and an `inUse` boolean flag. The `inUse` flag is determined by checking if the option's `key` exists within the current `qIndexArray` derived from the `qIndex` URL parameter. This ensures the checkboxes visually reflect the state in the URL.
58
+
59
+ 3. **Rendering Checkboxes:**
60
+ The component iterates over the `keyState` object and renders a `@folio/stripes/components` `Checkbox` for each entry. The `checked` prop of the checkbox is bound to the `inUse` flag from `keyState`, and the `label` is set accordingly.
61
+
62
+ 4. **Handling Changes:**
63
+ When a checkbox's state is changed by the user:
64
+
65
+ * The `onChange` handler determines if the key should be added or removed from the list of active keys.
66
+ * It updates a temporary copy of the `qIndexArray` (either adding the key with `push` or removing it with `splice`).
67
+ * It calls `setQIndex` (from `useQIndex`) with the modified array joined back into a comma-separated string, which updates the URL parameter.
68
+
69
+ 5. **Pagination Reset:**
70
+ The component also uses the `usePrevNextPagination` hook. If pagination is active (`currentPage` has a value), it calls `resetPage()` after updating the `qIndex`. This ensures that users are returned to the first page of results when the search indexes change, preventing potentially invalid page numbers for the new search scope.
@@ -0,0 +1,98 @@
1
+ import { useCallback, useEffect, useMemo, useState } from 'react';
2
+ import PropTypes from 'prop-types';
3
+
4
+ import isEqual from 'lodash/isEqual';
5
+
6
+ import { Checkbox } from '@folio/stripes/components';
7
+
8
+ import { usePrevNextPagination, useQIndex } from '../hooks';
9
+
10
+ import css from '../../../styles/SearchKeyControl.css';
11
+
12
+ /*
13
+ IMPORTANT -- This component is controlled by the qIndex, rather than the other way around.
14
+ It should inject values in/remove values from the qIndex, but not replace the entire thing.
15
+ */
16
+ const SearchKeyControl = ({
17
+ options = []
18
+ }) => {
19
+ // This component expects a qIndex comprising of a comma separated list
20
+ const [qIndex, setQIndex] = useQIndex();
21
+ const qIndexArray = useMemo(() => qIndex?.split(',')?.map(index => index.trim()) ?? [], [qIndex]);
22
+
23
+ // Memoise this process so keyState changes if and only if options/qIndex change
24
+ const createKeyState = useCallback(() => (
25
+ options.reduce((acc, curr) => {
26
+ acc[curr.key] = { inUse: qIndexArray?.includes(curr.key), label: curr.label ?? curr.key };
27
+ return acc;
28
+ }, {})
29
+ ), [options, qIndexArray]);
30
+
31
+ const [keyState, setKeyState] = useState(createKeyState());
32
+
33
+ // Keep keyState up to date as options/qIndex change
34
+ useEffect(() => {
35
+ const newKeyState = createKeyState();
36
+ if (!isEqual(newKeyState, keyState)) {
37
+ setKeyState(newKeyState);
38
+ }
39
+ }, [createKeyState, keyState]);
40
+
41
+ // Check to see if page param exists, and if it does then changing qIndex should reset it
42
+ const { currentPage, resetPage } = usePrevNextPagination({ defaultToPageOne: false });
43
+
44
+ return (
45
+ <>
46
+ <div className={css.searchKeyControlContainer}>
47
+ {
48
+ Object.entries(keyState).map(([key, value]) => {
49
+ /* At this point we have "key" corresponding to a searchKey option,
50
+ * and "value" an object of the shape
51
+ {
52
+ inUse: a bool determining if it is in use or not,
53
+ label: the label to display on the checkbox
54
+ }
55
+ */
56
+ return (
57
+ <Checkbox
58
+ key={`search-key-control-${key}`}
59
+ checked={value?.inUse}
60
+ className={css.searchKeyControlElement}
61
+ label={value?.label}
62
+ onChange={e => {
63
+ // If false, we must remove from the qIndex
64
+ if (!e.target.checked) {
65
+ const indexOfKey = qIndexArray.indexOf(key);
66
+ if (indexOfKey > -1) { // only splice array when item is found
67
+ qIndexArray.splice(indexOfKey, 1); // 2nd parameter means remove one item only
68
+ }
69
+ } else {
70
+ // If true, we need to add to qIndex
71
+ qIndexArray.push(key);
72
+ }
73
+
74
+ setQIndex(qIndexArray?.join(','));
75
+ if (currentPage) {
76
+ resetPage();
77
+ }
78
+ }}
79
+ />
80
+ );
81
+ })
82
+ }
83
+ </div>
84
+ </>
85
+ );
86
+ };
87
+
88
+ SearchKeyControl.propTypes = {
89
+ options: PropTypes.arrayOf(PropTypes.shape({
90
+ label: PropTypes.oneOfType([
91
+ PropTypes.string,
92
+ PropTypes.node
93
+ ]).isRequired,
94
+ key: PropTypes.string.isRequired
95
+ }))
96
+ };
97
+
98
+ export default SearchKeyControl;
@@ -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';