@k-int/stripes-kint-components 1.2.3 → 1.4.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 (60) hide show
  1. package/CHANGELOG.md +15 -5
  2. package/es/index.js +72 -0
  3. package/es/lib/NoResultsMessage/NoResultsMessage.js +89 -0
  4. package/es/lib/NoResultsMessage/index.js +15 -0
  5. package/es/lib/QueryTypedown/QueryTypedown.js +71 -0
  6. package/es/lib/QueryTypedown/index.js +15 -0
  7. package/es/lib/SASQLookupComponent/SASQLookupComponent.js +295 -0
  8. package/es/lib/SASQLookupComponent/index.js +15 -0
  9. package/es/lib/SASQRoute/SASQRoute.js +70 -0
  10. package/es/lib/SASQRoute/index.js +15 -0
  11. package/es/lib/SASQViewComponent/SASQViewComponent.js +71 -0
  12. package/es/lib/SASQViewComponent/index.js +15 -0
  13. package/es/lib/TypeDown/TypeDown.js +24 -6
  14. package/es/lib/Typedown/Typedown.js +225 -0
  15. package/es/lib/Typedown/index.js +15 -0
  16. package/es/lib/hooks/index.js +16 -0
  17. package/es/lib/hooks/typedownHooks/useTypedown.js +5 -1
  18. package/es/lib/hooks/useHelperApp.js +7 -1
  19. package/es/lib/hooks/useKiwtSASQuery.js +22 -1
  20. package/es/lib/hooks/useLocalStorageState.js +49 -0
  21. package/es/lib/hooks/useQIndex.js +75 -0
  22. package/es/lib/hooks/useRefdata.js +23 -6
  23. package/es/lib/utils/generateKiwtQuery.js +3 -93
  24. package/es/lib/utils/generateKiwtQueryParams.js +125 -0
  25. package/es/lib/utils/index.js +16 -0
  26. package/es/lib/utils/selectorSafe.js +1 -1
  27. package/package.json +1 -1
  28. package/src/index.js +31 -1
  29. package/src/lib/EditableRefdataList/README.md +1 -1
  30. package/src/lib/NoResultsMessage/NoResultsMessage.js +78 -0
  31. package/src/lib/NoResultsMessage/index.js +1 -0
  32. package/src/lib/QueryTypedown/QueryTypedown.js +33 -0
  33. package/src/lib/QueryTypedown/index.js +1 -0
  34. package/src/lib/SASQLookupComponent/SASQLookupComponent.js +282 -0
  35. package/src/lib/SASQLookupComponent/index.js +1 -0
  36. package/src/lib/SASQRoute/README.md +55 -0
  37. package/src/lib/SASQRoute/SASQRoute.js +63 -0
  38. package/src/lib/SASQRoute/index.js +1 -0
  39. package/src/lib/SASQViewComponent/SASQViewComponent.js +55 -0
  40. package/src/lib/SASQViewComponent/index.js +1 -0
  41. package/src/lib/TypeDown/README.md +1 -0
  42. package/src/lib/TypeDown/TypeDown.js +23 -4
  43. package/src/lib/Typedown/README.md +115 -0
  44. package/src/lib/Typedown/Typedown.js +234 -0
  45. package/src/lib/Typedown/index.js +1 -0
  46. package/src/lib/hooks/README.md +35 -1
  47. package/src/lib/hooks/index.js +3 -0
  48. package/src/lib/hooks/typedownHooks/useTypedown.js +3 -1
  49. package/src/lib/hooks/useHelperApp.js +5 -1
  50. package/src/lib/hooks/useKiwtSASQuery.js +9 -1
  51. package/src/lib/hooks/useLocalStorageState.js +17 -0
  52. package/src/lib/hooks/useQIndex.js +41 -0
  53. package/src/lib/hooks/useRefdata.js +23 -6
  54. package/src/lib/utils/README.md +39 -1
  55. package/src/lib/utils/generateKiwtQuery.js +3 -57
  56. package/src/lib/utils/generateKiwtQueryParams.js +67 -0
  57. package/src/lib/utils/index.js +3 -0
  58. package/src/lib/utils/selectorSafe.js +1 -1
  59. package/styles/NoResultsMessage.css +38 -0
  60. package/translations/stripes-kint-components/en.json +5 -1
@@ -0,0 +1,282 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { FormattedMessage } from 'react-intl';
4
+ import { useInfiniteQuery } from 'react-query';
5
+
6
+ import {
7
+ useNamespace,
8
+ useOkapiKy
9
+ } from '@folio/stripes/core';
10
+
11
+ import {
12
+ SearchAndSortQuery,
13
+ PersistedPaneset,
14
+ } from '@folio/stripes/smart-components';
15
+
16
+ import {
17
+ Button,
18
+ IconButton,
19
+ Icon,
20
+ MultiColumnList,
21
+ Pane,
22
+ PaneMenu,
23
+ SearchField,
24
+ } from '@folio/stripes/components';
25
+ import { NoResultsMessage } from '../NoResultsMessage';
26
+
27
+ import { generateKiwtQuery } from '../utils';
28
+ import { useKiwtSASQuery, useLocalStorageState } from '../hooks';
29
+
30
+
31
+ const SASQLookupComponent = ({
32
+ children,
33
+ fetchParameters = {},
34
+ FilterComponent = () => null,
35
+ history,
36
+ id,
37
+ location,
38
+ mainPaneProps,
39
+ match,
40
+ mclProps,
41
+ noSearchField,
42
+ path,
43
+ resultColumns = [],
44
+ RenderBody,
45
+ sasqProps,
46
+ }) => {
47
+ const { query, queryGetter, querySetter } = useKiwtSASQuery();
48
+ const { 0: namespace } = useNamespace();
49
+ const ky = useOkapiKy();
50
+
51
+ const filterPaneVisibileKey = `${namespace}-${id}-filterPaneVisibility`;
52
+
53
+ const fetchPageData = ({ pageParam = 0 }) => {
54
+ const queryMap = fetchParameters.SASQ_MAP;
55
+ queryMap.offset = pageParam;
56
+ return ky(`${fetchParameters.endpoint}${generateKiwtQuery(queryMap, query)}`).json();
57
+ };
58
+
59
+ const [filterPaneVisible, setFilterPaneVisible] = useLocalStorageState(filterPaneVisibileKey, true);
60
+ const toggleFilterPane = () => setFilterPaneVisible(!filterPaneVisible);
61
+
62
+ const {
63
+ data: totalData = {},
64
+ error,
65
+ isError,
66
+ isLoading,
67
+ fetchNextPage,
68
+ } = useInfiniteQuery(
69
+ [namespace, id, 'data', query],
70
+ fetchPageData
71
+ );
72
+
73
+ const data = totalData.pages?.reduce(
74
+ (acc, curr) => {
75
+ const newAcc = { ...acc };
76
+ for (const [key, value] of Object.entries(curr)) {
77
+ if (
78
+ key !== 'page' &&
79
+ key !== 'result' &&
80
+ acc[key] !== value
81
+ ) {
82
+ newAcc[key] = value;
83
+ }
84
+ }
85
+
86
+ const newResults = [...(acc.results ?? [])];
87
+ newResults.push(...(curr.results ?? []));
88
+ newAcc.results = newResults;
89
+
90
+ return newAcc;
91
+ },
92
+ {}
93
+ ) ?? {};
94
+
95
+ // TODO focus handling to stop redraw movin to top
96
+
97
+ const onNeedMoreData = (_askAmount, index) => {
98
+ fetchNextPage({ pageParam: index });
99
+ };
100
+
101
+ // Build the map of column definitions
102
+ const columnMapping = Object.fromEntries(
103
+ resultColumns.map(e => [e.propertyPath, e.label])
104
+ );
105
+
106
+ // Build the list of visible columns
107
+ const visibleColumns = resultColumns.map(e => e.propertyPath);
108
+
109
+ return (
110
+ <SearchAndSortQuery
111
+ initialSearchState={{ query: '' }}
112
+ queryGetter={queryGetter}
113
+ querySetter={querySetter}
114
+ {...sasqProps}
115
+ >
116
+ {
117
+ ({
118
+ activeFilters,
119
+ filterChanged,
120
+ getFilterHandlers,
121
+ getSearchHandlers,
122
+ onSort,
123
+ onSubmitSearch,
124
+ resetAll,
125
+ searchChanged,
126
+ searchValue
127
+ }) => {
128
+ const searchHandlers = getSearchHandlers();
129
+ const sortOrder = query.sort ?? '';
130
+ const disableReset = !filterChanged && !searchChanged;
131
+
132
+ const filterCount = activeFilters.string ? activeFilters.string.split(',').length : 0;
133
+
134
+ const TableBody = () => (
135
+ <MultiColumnList
136
+ autosize
137
+ columnMapping={columnMapping}
138
+ contentData={data?.results}
139
+ isEmptyMessage={
140
+ <NoResultsMessage
141
+ {...{
142
+ error,
143
+ isError,
144
+ isLoading,
145
+ filterPaneIsVisible: filterPaneVisible,
146
+ searchTerm: query.query,
147
+ toggleFilterPane
148
+ }}
149
+ />
150
+ }
151
+ isSelected={({ item }) => item.id === match?.params?.id}
152
+ onHeaderClick={onSort}
153
+ onNeedMoreData={onNeedMoreData}
154
+ onRowClick={(_e, rowData) => {
155
+ history.push(`${path}/${rowData?.id}${location?.search}`);
156
+ }}
157
+ pagingType="click"
158
+ sortDirection={sortOrder.startsWith('-') ? 'descending' : 'ascending'}
159
+ sortOrder={sortOrder.replace(/^-/, '').replace(/,.*/, '')}
160
+ totalCount={data.totalRecords}
161
+ virtualize
162
+ visibleColumns={visibleColumns}
163
+ {...mclProps}
164
+ />
165
+ );
166
+
167
+ const Body = RenderBody ?? TableBody;
168
+
169
+ return (
170
+ <PersistedPaneset
171
+ appId={namespace}
172
+ id={`${id}-paneset`}
173
+ >
174
+ {filterPaneVisible &&
175
+ <Pane
176
+ defaultWidth="20%"
177
+ lastMenu={
178
+ <PaneMenu>
179
+ <IconButton
180
+ icon="caret-left"
181
+ onClick={() => setFilterPaneVisible(false)}
182
+ />
183
+ </PaneMenu>
184
+ }
185
+ paneTitle={<FormattedMessage id="stripes-smart-components.searchAndFilter" />}
186
+ >
187
+ <form onSubmit={onSubmitSearch}>
188
+ {!noSearchField &&
189
+ <>
190
+ <SearchField
191
+ autoFocus
192
+ name="query"
193
+ onChange={searchHandlers.query}
194
+ onClear={searchHandlers.reset}
195
+ value={searchValue.query}
196
+ />
197
+ <Button
198
+ buttonStyle="primary"
199
+ disabled={!searchValue.query}
200
+ fullWidth
201
+ type="submit"
202
+ >
203
+ <FormattedMessage id="stripes-smart-components.search" />
204
+ </Button>
205
+ <Button
206
+ buttonStyle="none"
207
+ disabled={disableReset}
208
+ id="clickable-reset-all"
209
+ onClick={resetAll}
210
+ >
211
+ <Icon icon="times-circle-solid">
212
+ <FormattedMessage id="stripes-smart-components.resetAll" />
213
+ </Icon>
214
+ </Button>
215
+ </>
216
+ }
217
+ <FilterComponent
218
+ activeFilters={activeFilters.state}
219
+ filterChanged={filterChanged}
220
+ filterHandlers={getFilterHandlers()}
221
+ resetAll={resetAll}
222
+ searchChanged={searchChanged}
223
+ searchHandlers={getSearchHandlers()}
224
+ searchValue={searchValue}
225
+ />
226
+ </form>
227
+ </Pane>
228
+ }
229
+ <Pane
230
+ defaultWidth="fill"
231
+ firstMenu={!filterPaneVisible ?
232
+ <PaneMenu>
233
+ <IconButton
234
+ badgeCount={filterCount}
235
+ icon="caret-right"
236
+ onClick={() => setFilterPaneVisible(true)}
237
+ />
238
+ </PaneMenu>
239
+ :
240
+ null}
241
+ paneSub={<FormattedMessage id="stripes-kint-components.sasqLookupComponent.mainPane.found" values={{ total: data?.total }} />}
242
+ {...mainPaneProps}
243
+ >
244
+ <Body data={data} />
245
+ </Pane>
246
+ {children}
247
+ </PersistedPaneset>
248
+ );
249
+ }
250
+ }
251
+ </SearchAndSortQuery>
252
+ );
253
+ };
254
+
255
+ SASQLookupComponent.propTypes = {
256
+ children: PropTypes.oneOfType([
257
+ PropTypes.func,
258
+ PropTypes.node
259
+ ]),
260
+ fetchParameters: PropTypes.object,
261
+ FilterComponent: PropTypes.oneOfType([
262
+ PropTypes.func,
263
+ PropTypes.node
264
+ ]),
265
+ history: PropTypes.object,
266
+ id: PropTypes.string.isRequired,
267
+ location: PropTypes.object,
268
+ mainPaneProps: PropTypes.object,
269
+ match: PropTypes.object,
270
+ mclProps: PropTypes.object,
271
+ noSearchField: PropTypes.bool,
272
+ path: PropTypes.string.isRequired,
273
+ RenderBody: PropTypes.oneOfType([
274
+ PropTypes.func,
275
+ PropTypes.node
276
+ ]),
277
+ resource: PropTypes.object,
278
+ resultColumns: PropTypes.arrayOf(PropTypes.object),
279
+ sasqProps: PropTypes.object
280
+ };
281
+
282
+ export default SASQLookupComponent;
@@ -0,0 +1 @@
1
+ export { default } from './SASQLookupComponent';
@@ -0,0 +1,55 @@
1
+ # SASQRoute
2
+ A component designed to speed up the basic 3-pane layout setup process, SASQRoute is an all in one Routing, SASQ and MCL setup
3
+
4
+ ## Basic Usage
5
+ ```
6
+ import React from 'react';
7
+ import { FormattedMessage } from 'react-intl';
8
+
9
+ import { SASQRoute } from '@k-int/stripes-kint-components';
10
+ import ActionItem from '../components/ActionItem';
11
+
12
+ const ActionedRoute = ({ path }) => {
13
+
14
+ const fetchParameters = {
15
+ endpoint: "remote-sync/feedback/done",
16
+ itemEndpoint: "remote-sync/feedback",
17
+ SASQ_MAP: {
18
+ searchKey: 'description',
19
+ filterKeys: {
20
+ }
21
+ }
22
+ };
23
+
24
+ const resultColumns = [
25
+ { propertyPath: "selected", label: " " },
26
+ { propertyPath:"description", label: <FormattedMessage id="ui-remote-sync.prop.feedback.description" /> },
27
+ { propertyPath:"status", label: <FormattedMessage id="ui-remote-sync.prop.feedback.status" /> },
28
+ { propertyPath:"correlationId", label: <FormattedMessage id="ui-remote-sync.prop.feedback.correlationId" /> },
29
+ { propertyPath:"caseIndicator", label: <FormattedMessage id="ui-remote-sync.prop.feedback.caseIndicator" /> }
30
+ ];
31
+
32
+ return (
33
+ <SASQRoute
34
+ fetchParameters={fetchParameters}
35
+ id="actioned"
36
+ resultColumns={resultColumns}
37
+ path={path}
38
+ ViewComponent={ActionItem}
39
+ />
40
+ );
41
+ };
42
+ ```
43
+
44
+ NOTE - The following prop list is incomplete
45
+ ## Props
46
+
47
+ Name | Type | Description | default | required
48
+ --- | --- | --- | --- | ---
49
+ fetchParameters | object | An object containing the parameters needed to make the fetches necessary for both the table and the view components. `endpoint` contains the main fetch endpoint, which regularly will be used for both fetching all data, and also for fetching an individual item (It is assumed that this endpoint will have `/{id}` appended to it.) If `itemEndpoint` is provided that will be used instead (this will also have `/{:id}` appended to it). `SASQ_MAP` is an object of the shape taken by `generateKiwtQuery` | | ✓ |
50
+ id | string | A unique identifier for this route. IMPORTANT that this is unique, as it drives paneset logic | | ✓ |
51
+ path | string | The main path for this route. In the above example the path is `remote-sync/actioned`. This component will set up the main 3 pane layout under this path, and also the view pane route under `remote-sync/actioned/:id` | | ✓ |
52
+ resultColumns | array | An array containing objects with a `propertyPath` and `label`. These will be used to drive the MCL columns. | | ✓ |
53
+ ViewComponent | Element | The component to render on clicking a row entry. | | ✓ |
54
+
55
+
@@ -0,0 +1,63 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+
4
+ import {
5
+ Route
6
+ } from 'react-router-dom';
7
+
8
+ import SASQLookupComponent from '../SASQLookupComponent';
9
+ import SASQViewComponent from '../SASQViewComponent';
10
+
11
+ const SASQRoute = ({ path, fetchParameters, ...props }) => {
12
+ // Grab the SASQ_MAP and tweak it
13
+ const { SASQ_MAP = {} } = fetchParameters;
14
+
15
+ if (!SASQ_MAP.perPage) {
16
+ SASQ_MAP.perPage = 25;
17
+ }
18
+
19
+ SASQ_MAP.stats = true;
20
+
21
+ // Reinsert the SASQ_MAP
22
+ fetchParameters.SASQ_MAP = SASQ_MAP;
23
+
24
+ return (
25
+ <Route
26
+ path={`${path}/:id?`}
27
+ render={routeProps => {
28
+ return (
29
+ <SASQLookupComponent
30
+ {...routeProps}
31
+ fetchParameters={fetchParameters}
32
+ path={path}
33
+ {...props}
34
+ >
35
+ <Route
36
+ path={`${path}/:id`}
37
+ render={innerProps => (
38
+ <SASQViewComponent
39
+ {...innerProps}
40
+ fetchParameters={fetchParameters}
41
+ path={path}
42
+ ViewComponent={props.ViewComponent}
43
+ {...props}
44
+ />
45
+ )}
46
+ />
47
+ </SASQLookupComponent>
48
+ );
49
+ }}
50
+ />
51
+ );
52
+ };
53
+
54
+ SASQRoute.propTypes = {
55
+ fetchParameters: PropTypes.object,
56
+ path: PropTypes.string,
57
+ ViewComponent: PropTypes.oneOfType([
58
+ PropTypes.func,
59
+ PropTypes.node
60
+ ])
61
+ };
62
+
63
+ export default SASQRoute;
@@ -0,0 +1 @@
1
+ export { default } from './SASQRoute';
@@ -0,0 +1,55 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+
4
+ import { useQuery } from 'react-query';
5
+
6
+ import {
7
+ useNamespace,
8
+ useOkapiKy
9
+ } from '@folio/stripes/core';
10
+
11
+ const SASQViewComponent = ({
12
+ fetchParameters,
13
+ history,
14
+ location,
15
+ match,
16
+ path,
17
+ ViewComponent,
18
+ ...props
19
+ }) => {
20
+ const { 0: namespace } = useNamespace();
21
+
22
+ // If itemEndpoint is available, use that, otherwise use standard endpoint
23
+ const endpoint = fetchParameters?.itemEndpoint ?? fetchParameters?.endpoint;
24
+
25
+ const ky = useOkapiKy();
26
+ const { data = {} } = useQuery(
27
+ [namespace, 'data', 'view', match?.params?.id],
28
+ () => ky(`${endpoint}/${match?.params?.id}`).json(),
29
+ {
30
+ enabled: !!match?.params?.id
31
+ }
32
+ );
33
+
34
+ return (
35
+ <ViewComponent
36
+ onClose={() => history.push(`${path}${location.search}`)}
37
+ resource={data}
38
+ {...props}
39
+ />
40
+ );
41
+ };
42
+
43
+ SASQViewComponent.propTypes = {
44
+ fetchParameters: PropTypes.object,
45
+ history: PropTypes.object,
46
+ location: PropTypes.object,
47
+ match: PropTypes.object,
48
+ path: PropTypes.string.isRequired,
49
+ ViewComponent: PropTypes.oneOfType([
50
+ PropTypes.func,
51
+ PropTypes.node
52
+ ]),
53
+ };
54
+
55
+ export default SASQViewComponent;
@@ -0,0 +1 @@
1
+ export { default } from './SASQViewComponent';
@@ -0,0 +1 @@
1
+ DEPRECATED - DO NOT USE. You probably want Typedown
@@ -1,4 +1,4 @@
1
- import React, { useCallback, useState } from 'react';
1
+ import React, { useCallback, useEffect, useState } from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import classnames from 'classnames';
4
4
 
@@ -15,15 +15,25 @@ import css from '../../../styles/TypeDown.css';
15
15
  import { useTypedown, useTypedownData } from '../hooks/typedownHooks';
16
16
  import selectorSafe from '../utils/selectorSafe';
17
17
 
18
+ /* @deprecated
19
+ * Replaced by Typedown and QueryTypedown.
20
+ */
18
21
  const TypeDown = ({
19
22
  input,
20
23
  meta,
24
+ onChange,
21
25
  path,
22
26
  pathMutator,
23
27
  renderFooter = null,
24
28
  renderListItem = null,
25
29
  uniqueIdentificationPath = 'id'
26
30
  }) => {
31
+ useEffect(() => {
32
+ if (process.env.NODE_ENV === 'development') {
33
+ console.warn('TypeDown component is deprecated, you likely want either Typedown or QueryTypedown');
34
+ }
35
+ }, []);
36
+
27
37
  const selectedUniqueId = get(input.value, uniqueIdentificationPath);
28
38
 
29
39
  const [callPath, setCallPath] = useState(pathMutator(null, path));
@@ -61,6 +71,14 @@ const TypeDown = ({
61
71
  </div>
62
72
  ), [renderListItem, uniqueIdentificationPath]);
63
73
 
74
+ const handleChange = value => {
75
+ input.onChange(value);
76
+
77
+ if (typeof onChange === 'function') {
78
+ onChange(value);
79
+ }
80
+ };
81
+
64
82
  const dropDown = useCallback(() => {
65
83
  return (
66
84
  <div
@@ -86,7 +104,7 @@ const TypeDown = ({
86
104
  data-selected={isSelected}
87
105
  id={`typedown-button-[${index}]`}
88
106
  onClick={() => {
89
- input.onChange(d);
107
+ handleChange(d);
90
108
  handleNextFocus();
91
109
  }}
92
110
  onKeyDown={listKeyDownHandler}
@@ -132,9 +150,9 @@ const TypeDown = ({
132
150
  >
133
151
  <SearchField
134
152
  // Pass meta through so correct styling gets applied to the TextField
135
- {...meta}
136
153
  id="typedown-searchField"
137
154
  marginBottom0
155
+ meta={meta}
138
156
  onChange={e => {
139
157
  setCallPath(pathMutator(e.target.value, path));
140
158
  }}
@@ -186,7 +204,7 @@ const TypeDown = ({
186
204
  <IconButton
187
205
  className={css.clearItem}
188
206
  icon="times-circle-solid"
189
- onClick={() => input.onChange()}
207
+ onClick={() => handleChange()}
190
208
  />
191
209
  </div>
192
210
  }
@@ -197,6 +215,7 @@ const TypeDown = ({
197
215
  TypeDown.propTypes = {
198
216
  input: PropTypes.object,
199
217
  meta: PropTypes.object,
218
+ onChange: PropTypes.func,
200
219
  path: PropTypes.string,
201
220
  pathMutator: PropTypes.func,
202
221
  renderFooter: PropTypes.func,
@@ -0,0 +1,115 @@
1
+ ```
2
+ import React from 'react';
3
+
4
+ import { Button, Col, Pane, Row, TextField } from '@folio/stripes/components';
5
+ import { AppIcon } from '@folio/stripes/core';
6
+ import { generateKiwtQuery, TypeDown } from '@k-int/stripes-kint-components';
7
+
8
+ import { Form, Field, useFormState } from 'react-final-form';
9
+
10
+ const TestField = () => {
11
+ const pathMutator = (input, path) => {
12
+ const query = generateKiwtQuery(
13
+ {
14
+ searchKey: 'name,alternateNames.name',
15
+ stats: false
16
+ }, {
17
+ sort: 'name',
18
+ query: input,
19
+ }
20
+ );
21
+
22
+ return `${path}${query}`;
23
+ };
24
+
25
+ console.log("Current values: %o", useFormState()?.values)
26
+ return (
27
+ <Field
28
+ component={TypeDown}
29
+ name="test"
30
+ path="erm/sas"
31
+ pathMutator={pathMutator}
32
+ renderFooter={() => (
33
+ <>
34
+ <Button
35
+ id="footer button 1"
36
+ marginBottom0
37
+ onClick={() => {
38
+ alert('sup')
39
+ }}
40
+ type="button"
41
+ >
42
+ Hello 1
43
+ </Button>
44
+ <Button
45
+ id="footer button 2"
46
+ marginBottom0
47
+ onClick={() => {
48
+ alert('sup 2')
49
+ }}
50
+ type="button"
51
+ >
52
+ Hello 2
53
+ </Button>
54
+ </>
55
+ )}
56
+ renderListItem={agreement => {
57
+ return (
58
+ <>
59
+ <AppIcon
60
+ app="agreements"
61
+ size="small"
62
+ >
63
+ {agreement.name}
64
+ </AppIcon>
65
+ </>
66
+ );
67
+ }}
68
+ />
69
+ );
70
+ };
71
+
72
+
73
+ const TestComponent = () => {
74
+ return (
75
+ <Pane
76
+ defaultWidth="fill"
77
+ dismissible
78
+ id="test-typedown"
79
+ paneTitle={"Test typedown"}
80
+ >
81
+ <Form
82
+ enableReinitialize
83
+ navigationCheck
84
+ onSubmit={(values) => console.log("submitting: %o", values)}
85
+ subscription={{ values: true }}
86
+ >
87
+ {({ handleSubmit }) => {
88
+ return (
89
+ <form onSubmit={handleSubmit}>
90
+ <Row>
91
+ <Col xs={6}>
92
+ <TestField />
93
+ </Col>
94
+ <Col xs={6}>
95
+ <Field
96
+ component={TextField}
97
+ name="test2"
98
+ />
99
+ </Col>
100
+ </Row>
101
+ <Button
102
+ type="submit"
103
+ >
104
+ submit
105
+ </Button>
106
+ </form>
107
+ );
108
+ }}
109
+ </Form>
110
+ </Pane>
111
+ );
112
+ };
113
+
114
+ export default TestComponent;
115
+ ```