@performant-software/semantic-components 0.5.1 → 0.5.4

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 (32) hide show
  1. package/build/index.js +1 -1
  2. package/build/index.js.map +1 -1
  3. package/build/main.css +5 -0
  4. package/package.json +5 -5
  5. package/src/components/DataList.js +10 -2
  6. package/src/components/DataTableColumnSelector.js +37 -33
  7. package/src/components/DataView.css +4 -0
  8. package/src/components/DataView.js +407 -0
  9. package/src/components/MenuBar.css +0 -0
  10. package/src/components/MenuBar.js +82 -0
  11. package/src/components/MenuSidebar.css +0 -0
  12. package/src/components/MenuSidebar.js +84 -0
  13. package/src/components/ReferenceCodeDropdown.css +0 -0
  14. package/src/components/ReferenceCodeDropdown.js +118 -0
  15. package/src/components/ReferenceCodeModal.css +0 -0
  16. package/src/components/ReferenceCodeModal.js +32 -0
  17. package/src/components/ReferenceTableModal.css +0 -0
  18. package/src/components/ReferenceTableModal.js +67 -0
  19. package/src/components/ReferenceTablesList.css +0 -0
  20. package/src/components/ReferenceTablesList.js +44 -0
  21. package/src/i18n/en.json +32 -0
  22. package/src/index.js +7 -0
  23. package/types/components/DataList.js.flow +10 -2
  24. package/types/components/DataTableColumnSelector.js.flow +37 -33
  25. package/types/components/DataView.js.flow +302 -20
  26. package/types/components/MenuBar.js.flow +9 -4
  27. package/types/components/MenuSidebar.js.flow +18 -6
  28. package/types/components/ReferenceCodeDropdown.js.flow +118 -0
  29. package/types/components/ReferenceCodeModal.js.flow +32 -0
  30. package/types/components/ReferenceTableModal.js.flow +67 -0
  31. package/types/components/ReferenceTablesList.js.flow +44 -0
  32. package/types/index.js.flow +5 -0
@@ -1,42 +1,263 @@
1
1
  // @flow
2
2
 
3
3
  import axios from 'axios';
4
- import React, { useCallback, useEffect, useRef, useState } from 'react';
5
- import { Container } from 'semantic-ui-react';
4
+ import React, {
5
+ useCallback,
6
+ useEffect,
7
+ useMemo,
8
+ useRef,
9
+ useState
10
+ } from 'react';
11
+ import { Form, Grid, Modal } from 'semantic-ui-react';
6
12
  import _ from 'underscore';
7
- import MenuSidebar from './MenuSidebar';
13
+ import DataTable from './DataTable';
14
+ import DropdownButton from './DropdownButton';
15
+ import i18n from '../i18n/i18n';
8
16
  import MenuBar from './MenuBar';
9
- import ListTable from './ListTable';
17
+ import MenuSidebar from './MenuSidebar';
18
+ import useDataList, { SORT_ASCENDING, SORT_DESCENDING } from './DataList';
19
+ import './DataView.css';
20
+
21
+ import type { Column } from './DataTable';
22
+
23
+ type Sort = {
24
+ label: string,
25
+ value: string
26
+ };
10
27
 
11
28
  type Item = {
12
29
  id: string,
13
30
  label: string,
14
31
  group?: string,
15
- }
32
+ columns?: Array<Column>,
33
+ sorts?: Array<Sort>
34
+ };
16
35
 
17
36
  type Props = {
37
+ columnCount?: number,
18
38
  items: Array<Item>,
19
39
  layout: 'top' | 'left',
20
40
  title: string
21
41
  };
22
42
 
43
+ /**
44
+ * Sets up the List component used by the DataView component. This component is responsible for rendering the DataTable
45
+ * and handling sorting.
46
+ */
47
+ const List = useDataList((props) => {
48
+ const [activeSort, setActiveSort] = useState();
49
+
50
+ /**
51
+ * Sets the active sort object on the state.
52
+ *
53
+ * @type {(function(*): void)|*}
54
+ */
55
+ const onSortChange = useCallback((sort) => {
56
+ const newSort = { ...sort };
57
+
58
+ if (activeSort && activeSort.value === sort.value) {
59
+ newSort.direction = activeSort.direction === SORT_DESCENDING ? SORT_ASCENDING : SORT_DESCENDING;
60
+ }
61
+
62
+ setActiveSort(newSort);
63
+ }, [props.sorts, activeSort]);
64
+
65
+ /**
66
+ * Calls the onSort prop when the active sort is changed.
67
+ */
68
+ useEffect(() => {
69
+ if (activeSort) {
70
+ props.onSort(activeSort.value, activeSort.direction, props.page);
71
+ }
72
+ }, [activeSort]);
73
+
74
+ /**
75
+ * Calls the onSort prop when the component is loaded to initialize the data set.
76
+ */
77
+ useEffect(() => {
78
+ const { page } = props;
79
+
80
+ let { sortColumn = '', sortDirection = SORT_ASCENDING } = props;
81
+
82
+ if (!sortColumn) {
83
+ const defaultSort = _.first(props.sort);
84
+
85
+ if (defaultSort) {
86
+ sortColumn = defaultSort.value;
87
+
88
+ if (defaultSort.direction) {
89
+ sortDirection = defaultSort.direction;
90
+ }
91
+ }
92
+ }
93
+
94
+ props.onSort(sortColumn, sortDirection, page);
95
+ }, []);
96
+
97
+ return (
98
+ <DataTable
99
+ {...props}
100
+ renderListHeader={() => props.sorts && (
101
+ <DropdownButton
102
+ basic
103
+ icon={activeSort && activeSort.direction === SORT_DESCENDING
104
+ ? 'sort alphabet down'
105
+ : 'sort alphabet up'}
106
+ onChange={() => {}}
107
+ options={_.map(props.sorts, (sort) => ({
108
+ key: sort.value,
109
+ value: sort.value,
110
+ text: sort.label,
111
+ onClick: () => onSortChange(sort)
112
+ }))}
113
+ text={activeSort
114
+ ? i18n.t('DataView.labels.sortBy', { column: activeSort.label })
115
+ : i18n.t('DataView.labels.noSort')}
116
+ value={activeSort && activeSort.value}
117
+ />
118
+ )}
119
+ tableProps={{
120
+ ...props.tableProps || {},
121
+ celled: true
122
+ }}
123
+ />
124
+ );
125
+ });
126
+
23
127
  const DataView = (props: Props) => {
24
128
  const [activeItem, setActiveItem] = useState();
129
+ const [selectedRecord, setSelectedRecord] = useState();
130
+
25
131
  const [columns, setColumns] = useState([]);
26
132
  const [menu, setMenu] = useState([]);
27
133
  const [paddingLeft, setPaddingLeft] = useState();
134
+ const [key, setKey] = useState();
28
135
 
29
136
  const menuRef = useRef();
30
137
 
138
+ /**
139
+ * Sets the collection name based on the URL of the active item.
140
+ *
141
+ * @type {string}
142
+ */
143
+ const collectionName = useMemo(() => {
144
+ let name = '';
145
+
146
+ if (activeItem) {
147
+ name = activeItem.url.substr(activeItem.url.lastIndexOf('/') + 1, activeItem.url.length);
148
+ }
149
+
150
+ return name;
151
+ }, [activeItem]);
152
+
153
+ /**
154
+ * Merges the calculated columns with the columns passed with the active item. The idea is to allow customization
155
+ * of the columns by the implementing component, but not require it.
156
+ *
157
+ * @type {(function(*): ([]|*))|*}
158
+ */
159
+ const mergeColumns = useCallback((items) => {
160
+ if (!activeItem) {
161
+ return [];
162
+ }
163
+
164
+ // Build the list of columns based on the unique set of properties
165
+ const keys = _.chain(items)
166
+ .map((item) => _.keys(item))
167
+ .flatten()
168
+ .uniq()
169
+ .value();
170
+
171
+ // Merge any columns provided by the implementing component by name
172
+ let cols = _.map(keys, (col, index) => {
173
+ const columnValue = _.findWhere(activeItem.columns, { name: col }) || {};
174
+
175
+ const defaultValue = {
176
+ name: col,
177
+ label: col.trim().replace(/^\w/, (c) => c.toUpperCase()).replaceAll('_', ' '),
178
+ sortable: false,
179
+ resolve: (item) => resolveValue(item, col),
180
+ hidden: index > props.columnCount,
181
+ index
182
+ };
183
+
184
+ return _.extend(defaultValue, columnValue);
185
+ });
186
+
187
+ // Include any columns provided by the implementing component that do not exist on the object (i.e. extra columns)
188
+ cols = [
189
+ ...cols,
190
+ ..._.reject(activeItem.columns, (col) => _.contains(keys, col.name))
191
+ ];
192
+
193
+ // Sort the columns by the index property
194
+ return _.sortBy(cols, 'index');
195
+ }, [activeItem]);
196
+
197
+ /**
198
+ * Calls the "show" URL for the passed item. It's possible that the API does not implement a public "show" route. In
199
+ * this case and error is expected and the passed item will be set as the selected record.
200
+ *
201
+ * @type {(function(*): void)|*}
202
+ */
203
+ const onItemSelection = useCallback((item) => {
204
+ if (activeItem) {
205
+ axios
206
+ .get(`${activeItem.url}/${item.id}`)
207
+ .then(({ data }) => {
208
+ const itemKey = _.first(_.keys(data));
209
+ setSelectedRecord(data[itemKey]);
210
+ })
211
+ .catch(() => {
212
+ setSelectedRecord(item);
213
+ });
214
+ }
215
+ }, [activeItem]);
216
+
217
+ /**
218
+ * Calls the "index" URL to initialize the data set.
219
+ *
220
+ * @type {function(*): *}
221
+ */
31
222
  const onLoad = useCallback((params) => {
223
+ let promise;
224
+
32
225
  if (activeItem) {
33
- axios.get
34
- fetch(activeItem.url).then((response) => {
35
- console.log(response);
36
- });
226
+ promise = axios
227
+ .get(activeItem.url, { params })
228
+ .then((response) => {
229
+ const items = response.data[collectionName];
230
+ setColumns(mergeColumns(items));
231
+
232
+ return response;
233
+ });
234
+ } else {
235
+ promise = Promise.resolve();
37
236
  }
237
+
238
+ return promise;
38
239
  }, [activeItem]);
39
240
 
241
+ /**
242
+ * Returns the value of the passed attribute on the passed item. This function handles use cases for special
243
+ * types of objects.
244
+ *
245
+ * @type {function(*, *): *}
246
+ */
247
+ const resolveValue = useCallback((item, attribute) => {
248
+ let value = item[attribute];
249
+
250
+ if (_.isArray(value)) {
251
+ value = _.size(value);
252
+ } else if (_.isBoolean(value)) {
253
+ value = Boolean(value).toString();
254
+ } else if (_.isObject(value)) {
255
+ value = value[_.first(_.keys(value))];
256
+ }
257
+
258
+ return value;
259
+ }, []);
260
+
40
261
  /**
41
262
  * Re-formats the passed items as a menu object. If we're grouping the menu items, items will be added as
42
263
  * a child property of the group object.
@@ -66,7 +287,7 @@ const DataView = (props: Props) => {
66
287
  */
67
288
  useEffect(() => {
68
289
  if (props.items && props.items.length) {
69
- setActiveItem(props.items[0]);
290
+ setActiveItem(_.first(props.items));
70
291
  }
71
292
  }, [props.items]);
72
293
 
@@ -79,6 +300,24 @@ const DataView = (props: Props) => {
79
300
  }
80
301
  }, [menuRef, props.layout]);
81
302
 
303
+ /**
304
+ * Calculates the key value when the active item or columns are changed. This is done to force re-rendering
305
+ * of the List component when the columns are loaded via the API.
306
+ */
307
+ useEffect(() => {
308
+ const keys = [];
309
+
310
+ if (activeItem) {
311
+ keys.push(activeItem.id);
312
+ }
313
+
314
+ if (columns && columns.length) {
315
+ keys.push(..._.pluck(columns, 'name'));
316
+ }
317
+
318
+ setKey(keys.join(''));
319
+ }, [activeItem, columns]);
320
+
82
321
  return (
83
322
  <div
84
323
  className='data-view'
@@ -100,26 +339,69 @@ const DataView = (props: Props) => {
100
339
  items={menu}
101
340
  />
102
341
  )}
103
- <Container
342
+ <div
104
343
  style={{
105
- paddingLeft
344
+ marginLeft: paddingLeft
106
345
  }}
107
346
  >
108
347
  { activeItem && (
109
- <ListTable
110
- collectionName='items'
348
+ <List
349
+ actions={[{
350
+ name: 'details',
351
+ icon: 'info',
352
+ onClick: onItemSelection
353
+ }]}
354
+ collectionName={collectionName}
111
355
  columns={columns}
112
- onLoad={(params) => Api.onLoad(_.extend(params, {
113
- items,
114
- perPage: number('Per page', 10)
115
- }))}
356
+ key={key}
357
+ onLoad={onLoad}
358
+ perPageOptions={[10, 25, 50, 100]}
116
359
  searchable
360
+ sorts={activeItem.sorts}
117
361
  />
118
362
  )}
119
- <div>Content</div>
120
- </Container>
363
+ </div>
364
+ { selectedRecord && (
365
+ <Modal
366
+ as={Form}
367
+ centered={false}
368
+ className='data-view-modal'
369
+ closeIcon
370
+ onClose={() => setSelectedRecord(null)}
371
+ open
372
+ >
373
+ <Modal.Header
374
+ content={i18n.t('DataView.labels.details')}
375
+ />
376
+ <Modal.Content>
377
+ <Grid
378
+ columns={3}
379
+ doubling
380
+ >
381
+ { _.map(mergeColumns([selectedRecord]), (column) => (
382
+ <Grid.Column
383
+ as={Form.Field}
384
+ key={column.name}
385
+ >
386
+ <span
387
+ className='label'
388
+ >
389
+ { column.label }
390
+ </span>
391
+ { resolveValue(selectedRecord, column.name) }
392
+ </Grid.Column>
393
+ ))}
394
+ </Grid>
395
+ </Modal.Content>
396
+ </Modal>
397
+ )}
121
398
  </div>
122
399
  );
123
400
  };
124
401
 
402
+ DataView.defaultProps = {
403
+ columnCount: 5,
404
+ layout: 'left'
405
+ };
406
+
125
407
  export default DataView;
@@ -24,8 +24,11 @@ const MenuBar = ({ header, items, ...props }: Props) => {
24
24
  *
25
25
  * @returns {JSX.Element}
26
26
  */
27
- const renderDropdownItem = useCallback((item) => (
28
- <Dropdown.Item {...item} />
27
+ const renderDropdownItem = useCallback((item, index) => (
28
+ <Dropdown.Item
29
+ key={index}
30
+ {...item}
31
+ />
29
32
  ), []);
30
33
 
31
34
  /**
@@ -33,8 +36,9 @@ const MenuBar = ({ header, items, ...props }: Props) => {
33
36
  *
34
37
  * @returns {JSX.Element}
35
38
  */
36
- const renderMenuItem = useCallback((item) => (
39
+ const renderMenuItem = useCallback((item, index) => (
37
40
  <Menu.Item
41
+ key={index}
38
42
  {...item}
39
43
  />
40
44
  ), []);
@@ -44,9 +48,10 @@ const MenuBar = ({ header, items, ...props }: Props) => {
44
48
  *
45
49
  * @returns {JSX.Element}
46
50
  */
47
- const renderDropdown = useCallback((item) => (
51
+ const renderDropdown = useCallback((item, index) => (
48
52
  <DropdownMenu
49
53
  item
54
+ key={index}
50
55
  text={item.content}
51
56
  >
52
57
  { _.map(item.items, (i) => (i.items ? renderDropdown(i) : renderDropdownItem(i)))}
@@ -4,24 +4,35 @@ import React, { useCallback } from 'react';
4
4
  import {
5
5
  Header,
6
6
  Menu,
7
+ Ref,
7
8
  type HeaderProps,
8
- type MenuItemProps, Ref
9
+ type MenuItemProps
9
10
  } from 'semantic-ui-react';
10
11
  import _ from 'underscore';
11
12
 
12
13
  type Props = {
14
+ contextRef: {
15
+ current: ?HTMLElement
16
+ },
13
17
  header: HeaderProps,
14
18
  items: Array<HeaderProps | MenuItemProps>
15
19
  };
16
20
 
17
- const MenuSidebar = ({ header, items, ...props }: Props) => {
21
+ const MenuSidebar = ({
22
+ contextRef,
23
+ header,
24
+ items,
25
+ ...props
26
+ }: Props) => {
18
27
  /**
19
28
  * Renders the passed item as a menu.
20
29
  *
21
30
  * @type {unknown}
22
31
  */
23
- const renderMenu = useCallback((item) => (
24
- <Menu.Item>
32
+ const renderMenu = useCallback((item, index) => (
33
+ <Menu.Item
34
+ key={index}
35
+ >
25
36
  <Menu.Header
26
37
  {...item}
27
38
  />
@@ -36,8 +47,9 @@ const MenuSidebar = ({ header, items, ...props }: Props) => {
36
47
  *
37
48
  * @type {unknown}
38
49
  */
39
- const renderMenuItem = useCallback((item) => (
50
+ const renderMenuItem = useCallback((item, index) => (
40
51
  <Menu.Item
52
+ key={index}
41
53
  {...item}
42
54
  />
43
55
  ), []);
@@ -51,7 +63,7 @@ const MenuSidebar = ({ header, items, ...props }: Props) => {
51
63
 
52
64
  return (
53
65
  <Ref
54
- innerRef={props.contextRef}
66
+ innerRef={contextRef}
55
67
  >
56
68
  <Menu
57
69
  {...props}
@@ -0,0 +1,118 @@
1
+ // @flow
2
+
3
+ import { ReferenceCodesService } from '@performant-software/shared-components';
4
+ import React, {
5
+ useCallback,
6
+ useEffect,
7
+ useMemo,
8
+ useState
9
+ } from 'react';
10
+ import _ from 'underscore';
11
+ import { Dropdown } from 'semantic-ui-react';
12
+
13
+ type Item = {
14
+ reference_table_id: number,
15
+ key: string
16
+ };
17
+
18
+ type Props = {
19
+ fluid?: boolean,
20
+ multiple?: boolean,
21
+ onChange: (item: Item) => void,
22
+ placeholder?: string,
23
+ referenceTable: string,
24
+ value: Item | Array<Item>
25
+ };
26
+
27
+ const ReferenceCodeDropdown = (props: Props) => {
28
+ const [loading, setLoading] = useState(false);
29
+ const [options, setOptions] = useState([]);
30
+
31
+ /**
32
+ * Converts the passed ID to a reference code item.
33
+ *
34
+ * @type {function(*): {reference_code_id: *}}
35
+ */
36
+ const toItem = useCallback((id) => ({
37
+ reference_code_id: id
38
+ }), []);
39
+
40
+ /**
41
+ * Converts the passed reference code to a dropdown option.
42
+ *
43
+ * @type {function(*): {text: *, value: *, key: *}}
44
+ */
45
+ const toOption = useCallback((referenceCode) => ({
46
+ key: referenceCode.id,
47
+ value: referenceCode.id,
48
+ text: referenceCode.name
49
+ }), []);
50
+
51
+ /**
52
+ * Converts the selected values and calls the onChange prop.
53
+ *
54
+ * @type {(function(*, {value: *}): void)|*}
55
+ */
56
+ const onChange = useCallback((e, { value }) => {
57
+ let values;
58
+
59
+ if (props.multiple) {
60
+ values = value;
61
+ } else {
62
+ values = _.compact([value]);
63
+ }
64
+
65
+ props.onChange(_.map(values, toItem));
66
+ }, [toItem, props.multiple, props.onChange]);
67
+
68
+ /**
69
+ * Sets the "value" variable for the Dropdown component.
70
+ */
71
+ const value = useMemo(() => {
72
+ const v = _.pluck(_.filter(props.value, (x) => !x._destroy), 'reference_code_id');
73
+ return props.multiple ? v : _.first(v);
74
+ }, [props.multiple, props.value]);
75
+
76
+ /**
77
+ * Loads the list of reference codes from the server.
78
+ */
79
+ useEffect(() => {
80
+ setLoading(true);
81
+
82
+ const params = {
83
+ per_page: 0,
84
+ reference_table: props.referenceTable,
85
+ sort_by: 'name'
86
+ };
87
+
88
+ ReferenceCodesService
89
+ .fetchAll(params)
90
+ .then(({ data }) => setOptions(_.map(data.reference_codes, toOption)))
91
+ .finally(() => setLoading(false));
92
+ }, [toOption]);
93
+
94
+ return (
95
+ <Dropdown
96
+ clearable
97
+ disabled={loading}
98
+ fluid={props.fluid}
99
+ loading={loading}
100
+ multiple={props.multiple}
101
+ onChange={onChange}
102
+ options={options}
103
+ placeholder={props.placeholder}
104
+ search
105
+ selection
106
+ selectOnBlur={false}
107
+ value={value}
108
+ />
109
+ );
110
+ };
111
+
112
+ ReferenceCodeDropdown.defaultProps = {
113
+ fluid: true,
114
+ multiple: false,
115
+ placeholder: undefined
116
+ };
117
+
118
+ export default ReferenceCodeDropdown;
@@ -0,0 +1,32 @@
1
+ // @flow
2
+
3
+ import type { EditContainerProps } from '@performant-software/shared-components';
4
+ import React from 'react';
5
+ import { Form, Modal } from 'semantic-ui-react';
6
+ import i18n from '../i18n/i18n';
7
+
8
+ const ReferenceCodeModal = (props: EditContainerProps) => (
9
+ <Modal
10
+ as={Form}
11
+ centered={false}
12
+ open
13
+ >
14
+ <Modal.Header
15
+ content={props.item.id
16
+ ? i18n.t('ReferenceCodeModal.title.edit')
17
+ : i18n.t('ReferenceCodeModal.title.add')}
18
+ />
19
+ <Modal.Content>
20
+ <Form.Input
21
+ error={props.isError('name')}
22
+ label={i18n.t('ReferenceCodeModal.labels.name')}
23
+ onChange={props.onTextInputChange.bind(this, 'name')}
24
+ required={props.isRequired('name')}
25
+ value={props.item.name}
26
+ />
27
+ </Modal.Content>
28
+ { props.children }
29
+ </Modal>
30
+ );
31
+
32
+ export default ReferenceCodeModal;
@@ -0,0 +1,67 @@
1
+ // @flow
2
+
3
+ import type { EditContainerProps } from '@performant-software/shared-components';
4
+ import React from 'react';
5
+ import { Form, Header, Modal } from 'semantic-ui-react';
6
+ import EmbeddedList from './EmbeddedList';
7
+ import ReferenceCodeModal from './ReferenceCodeModal';
8
+ import i18n from '../i18n/i18n';
9
+
10
+ const ReferenceTableModal = (props: EditContainerProps) => (
11
+ <Modal
12
+ as={Form}
13
+ centered={false}
14
+ className='reference-table-modal'
15
+ open
16
+ >
17
+ <Modal.Header
18
+ content={props.item.id
19
+ ? i18n.t('ReferenceTableModal.title.edit')
20
+ : i18n.t('ReferenceTableModal.title.add')}
21
+ />
22
+ <Modal.Content>
23
+ <Form.Input
24
+ error={props.isError('name')}
25
+ label={i18n.t('ReferenceTableModal.labels.name')}
26
+ onChange={props.onTextInputChange.bind(this, 'name')}
27
+ required={props.isRequired('name')}
28
+ value={props.item.name}
29
+ />
30
+ <Form.Input
31
+ error={props.isError('key')}
32
+ label={i18n.t('ReferenceTableModal.labels.key')}
33
+ onChange={props.onTextInputChange.bind(this, 'key')}
34
+ required={props.isRequired('key')}
35
+ value={props.item.key}
36
+ />
37
+ <Header
38
+ content={i18n.t('ReferenceTableModal.labels.referenceCodes')}
39
+ />
40
+ <EmbeddedList
41
+ actions={[{
42
+ name: 'edit'
43
+ }, {
44
+ name: 'copy'
45
+ }, {
46
+ name: 'delete'
47
+ }]}
48
+ columns={[{
49
+ name: 'name',
50
+ label: i18n.t('ReferenceTableModal.referenceCodes.columns.name')
51
+ }]}
52
+ items={props.item.reference_codes}
53
+ modal={{
54
+ component: ReferenceCodeModal,
55
+ props: {
56
+ required: ['name']
57
+ }
58
+ }}
59
+ onDelete={props.onDeleteChildAssociation.bind(this, 'reference_codes')}
60
+ onSave={props.onSaveChildAssociation.bind(this, 'reference_codes')}
61
+ />
62
+ </Modal.Content>
63
+ { props.children }
64
+ </Modal>
65
+ );
66
+
67
+ export default ReferenceTableModal;