@k-int/stripes-kint-components 1.2.4 → 1.5.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 (62) hide show
  1. package/CHANGELOG.md +18 -8
  2. package/es/index.js +78 -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 +24 -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/useKiwtFieldArray.js +109 -0
  20. package/es/lib/hooks/useKiwtSASQuery.js +22 -1
  21. package/es/lib/hooks/useLocalStorageState.js +49 -0
  22. package/es/lib/hooks/useQIndex.js +75 -0
  23. package/es/lib/hooks/useRefdata.js +23 -6
  24. package/es/lib/utils/generateKiwtQuery.js +3 -93
  25. package/es/lib/utils/generateKiwtQueryParams.js +125 -0
  26. package/es/lib/utils/index.js +16 -0
  27. package/es/lib/utils/selectorSafe.js +1 -1
  28. package/package.json +1 -1
  29. package/src/index.js +32 -1
  30. package/src/lib/EditableRefdataList/README.md +1 -1
  31. package/src/lib/NoResultsMessage/NoResultsMessage.js +78 -0
  32. package/src/lib/NoResultsMessage/index.js +1 -0
  33. package/src/lib/QueryTypedown/QueryTypedown.js +33 -0
  34. package/src/lib/QueryTypedown/index.js +1 -0
  35. package/src/lib/SASQLookupComponent/SASQLookupComponent.js +282 -0
  36. package/src/lib/SASQLookupComponent/index.js +1 -0
  37. package/src/lib/SASQRoute/README.md +55 -0
  38. package/src/lib/SASQRoute/SASQRoute.js +63 -0
  39. package/src/lib/SASQRoute/index.js +1 -0
  40. package/src/lib/SASQViewComponent/SASQViewComponent.js +55 -0
  41. package/src/lib/SASQViewComponent/index.js +1 -0
  42. package/src/lib/TypeDown/README.md +1 -0
  43. package/src/lib/TypeDown/TypeDown.js +23 -4
  44. package/src/lib/Typedown/README.md +115 -0
  45. package/src/lib/Typedown/Typedown.js +234 -0
  46. package/src/lib/Typedown/index.js +1 -0
  47. package/src/lib/hooks/README.md +109 -2
  48. package/src/lib/hooks/index.js +3 -0
  49. package/src/lib/hooks/typedownHooks/useTypedown.js +3 -1
  50. package/src/lib/hooks/useHelperApp.js +5 -1
  51. package/src/lib/hooks/useKiwtFieldArray.js +63 -0
  52. package/src/lib/hooks/useKiwtSASQuery.js +9 -1
  53. package/src/lib/hooks/useLocalStorageState.js +17 -0
  54. package/src/lib/hooks/useQIndex.js +41 -0
  55. package/src/lib/hooks/useRefdata.js +23 -6
  56. package/src/lib/utils/README.md +39 -1
  57. package/src/lib/utils/generateKiwtQuery.js +3 -57
  58. package/src/lib/utils/generateKiwtQueryParams.js +67 -0
  59. package/src/lib/utils/index.js +3 -0
  60. package/src/lib/utils/selectorSafe.js +1 -1
  61. package/styles/NoResultsMessage.css +38 -0
  62. package/translations/stripes-kint-components/en.json +5 -1
@@ -0,0 +1,17 @@
1
+ import React, { useState } from 'react';
2
+ import { useLocalStorage, writeStorage } from '@rehooks/local-storage';
3
+
4
+
5
+ const useLocalStorageState = (key, defaultValue) => {
6
+ const [local] = useLocalStorage(key, defaultValue);
7
+ const [localState, setLocalState] = useState(local);
8
+
9
+ const setLocalStorageState = (newValue) => {
10
+ setLocalState(newValue);
11
+ writeStorage(key, newValue);
12
+ };
13
+
14
+ return [localState, setLocalStorageState];
15
+ };
16
+
17
+ export default useLocalStorageState;
@@ -0,0 +1,41 @@
1
+ import React, { useEffect, useState } from 'react';
2
+ import { useHistory, useLocation } from 'react-router-dom';
3
+ import queryString from 'query-string';
4
+
5
+ const useQIndex = () => {
6
+ const history = useHistory();
7
+ const location = useLocation();
8
+
9
+ const query = queryString.parse(location.search);
10
+ const [currentQindex, setCurrentQindex] = useState(query?.qindex);
11
+
12
+ const handleQindexChange = (newQindex) => {
13
+ setCurrentQindex(newQindex);
14
+ if (newQindex !== query?.qindex) {
15
+ const newQuery = {
16
+ ...query,
17
+ qindex: newQindex
18
+ };
19
+
20
+ history.push({
21
+ pathname: location.pathname,
22
+ search: `?${queryString.stringify(newQuery)}`
23
+ });
24
+ }
25
+ };
26
+
27
+ useEffect(() => {
28
+ if (currentQindex !== query?.qindex) {
29
+ setCurrentQindex(query?.qindex);
30
+ }
31
+ }, [
32
+ currentQindex,
33
+ history,
34
+ location,
35
+ query
36
+ ]);
37
+
38
+ return [currentQindex, handleQindexChange];
39
+ };
40
+
41
+ export default useQIndex;
@@ -4,25 +4,42 @@ import PropTypes from 'prop-types';
4
4
  import { useQuery } from 'react-query';
5
5
  import { useOkapiKy } from '@folio/stripes/core';
6
6
 
7
+ import { generateKiwtQuery } from '../utils';
8
+
7
9
  const useRefdata = ({
8
10
  endpoint,
9
11
  desc,
10
12
  queryParams,
11
- returnQueryObject = false
13
+ returnQueryObject = false,
14
+ options = {
15
+ filterKeys: {
16
+ DescKey: 'desc'
17
+ },
18
+ stats: false,
19
+ max: 100
20
+ }
12
21
  }) => {
13
22
  const ky = useOkapiKy();
14
- let path = endpoint;
23
+
24
+ const nsValues = {};
25
+
26
+ /* Desc will tend to contain a '.', which will throw off generateKiwtQuery.
27
+ * To combat this we can insert another Desc. at the beginning to act as the filtername, so
28
+ * Funder.Name => Desc.desc=Funder.Name. This way "Desc" acts as the filterName, and without a filterKey
29
+ * "desc=Funder.Name" is passed wholesale to the query.
30
+ */
15
31
 
16
32
  if (Array.isArray(desc)) {
17
33
  // If we have an array, append a desc filter for each desc given
18
- desc.forEach((d, index) => {
19
- path += `${index === 0 ? '?filters=' : '%7C%7C'}desc=${d}`;
20
- });
34
+ nsValues.filters = desc.map(d => `DescKey.${d}`).join(',');
21
35
  } else if (desc) {
22
36
  // If we just have a string, append a single desc filter
23
- path += `?filters=desc=${desc}`;
37
+ nsValues.filters = `DescKey.${desc}`;
24
38
  }
25
39
 
40
+ const query = generateKiwtQuery(options, nsValues);
41
+ const path = `${endpoint}${query}`;
42
+
26
43
  const queryObject = useQuery(
27
44
  ['stripes-kint-components', 'useRefdata', 'refdata', desc ?? ''],
28
45
  () => ky(path).json(),
@@ -29,5 +29,43 @@ import { generateKiwtQuery } from '@k-int/stripes-kint-components'
29
29
  ### Props
30
30
  Name | Type | Description | default | required
31
31
  --- | --- | --- | --- | ---
32
- options | object | An object with keys: `searchKey`, `filterKeys`, `sortKeys` and `stats`, which maps the incoming nsValues objects to a KIWT query. | | ✓ |
32
+ options | object | An object with keys: `searchKey`, `filterKeys`, `sortKeys` and `stats`, which maps the incoming nsValues objects to a KIWT query. You can also pass arbitrary `key`/`value` pairs to append `key==value` onto the query. | | ✓ |
33
+ nsValues | object | An object containing the actual query parameters to become the mapped KIWT query | | ✓ |
34
+
35
+ ## generateKiwtQueryParams
36
+ A util function for generating an array of "KIWT" (K-Int Web-Toolkit) style backend query parameters from a SASQ query object
37
+ ### Basic usage
38
+ ```
39
+ import { generateKiwtQueryParams } from '@k-int/stripes-kint-components'
40
+ ...
41
+ const nsValues = {
42
+ query: 'test',
43
+ filters: 'requestStatus.completed,journalVolume.test1,startDate.startDate>=2021-10-28'
44
+ }
45
+
46
+ const options = {
47
+ searchKey: 'request.name',
48
+ filterKeys: {
49
+ requestStatus: 'requestStatus.value,
50
+ journalVolume: 'journalVolume'
51
+ }
52
+ }
53
+
54
+ const queryArray = generateKiwtQueryParams(options, nsValues)
55
+
56
+ // We'd expect queryArray === [
57
+ "match=request.name",
58
+ "term=test",
59
+ "filters=requestStatus.value==completed",
60
+ "filters=journalVolume==test1",
61
+ "filters=startDate%3E=2021-10-28",
62
+ "stats=true"
63
+ ]
64
+ ...
65
+ ```
66
+
67
+ ### Props
68
+ Name | Type | Description | default | required
69
+ --- | --- | --- | --- | ---
70
+ options | object | An object with keys: `searchKey`, `filterKeys`, `sortKeys` and `stats`, which maps the incoming nsValues objects to an KIWT query array. You can also pass arbitrary `key`/`value` pairs to append `key==value` onto the query. | | ✓ |
33
71
  nsValues | object | An object containing the actual query parameters to become the mapped KIWT query | | ✓ |
@@ -1,61 +1,7 @@
1
- const generateKiwtQuery = (options, nsValues) => {
2
- const { query, filters, sort } = nsValues;
3
- const {
4
- searchKey = '',
5
- /* Assumtion made that if no filterKey is provided then the given filterValues for that key are standalaone, ie require no comparator or key */
6
- filterKeys = {},
7
- sortKeys = {},
8
- stats = true,
9
- } = options;
10
-
11
- const paramsArray = [];
12
-
13
- if (query) {
14
- paramsArray.push(...searchKey.split(',')?.map(m => `match=${m}`));
15
- paramsArray.push(`term=${query}`);
16
- }
17
-
18
- if (filters) {
19
- const filterMap = {};
20
- filters.split(',').forEach(filter => {
21
- const [filterName, ...rest] = filter.split('.');
22
- const filterValue = rest.join('.');
23
-
24
- if (filterMap[filterName] === undefined) filterMap[filterName] = [];
25
- filterMap[filterName].push(filterValue);
26
- });
27
-
28
- // We now have a filterMap of shape { status: ['active', 'cancelled'], type: ['local'] }
29
- Object.entries(filterMap).forEach(([filterName, filterValues]) => {
30
- const filterKey = filterKeys[filterName];
31
-
32
- if (!filterKey) {
33
- // These filters have no key mapping so we just pass the values to the backend as-is.
34
- paramsArray.push(...filterValues?.map(f => `filters=${f}`));
35
- } else {
36
- const filterString = filterValues.map(v => `${filterKey}==${v}`).join('||');
37
- paramsArray.push(`filters=${filterString}`);
38
- }
39
- });
40
- }
41
-
42
- if (sort) {
43
- paramsArray.push(...sort.split(',').map(sortKey => {
44
- const descending = sortKey.startsWith('-');
45
- let term = sortKey.replace('-', '');
46
-
47
- if (term in sortKeys) {
48
- term = term.replace(term, sortKeys[term]);
49
- }
50
-
51
- return `sort=${term};${descending ? 'desc' : 'asc'}`;
52
- }));
53
- }
54
-
55
- if (stats) {
56
- paramsArray.push('stats=true');
57
- }
1
+ import generateKiwtQueryParams from './generateKiwtQueryParams';
58
2
 
3
+ const generateKiwtQuery = (options, nsValues) => {
4
+ const paramsArray = generateKiwtQueryParams(options, nsValues);
59
5
  return paramsArray.length ? '?' + paramsArray.map(p => encodeURI(p)).join('&') : '';
60
6
  };
61
7
 
@@ -0,0 +1,67 @@
1
+ const generateKiwtQueryParams = (options, nsValues) => {
2
+ const { qindex, query, filters, sort } = nsValues;
3
+ const {
4
+ searchKey = '',
5
+ /* Assumtion made that if no filterKey is provided then the given filterValues for that key are standalaone, ie require no comparator or key */
6
+ filterKeys = {},
7
+ sortKeys = {},
8
+ stats = true,
9
+ ...rest
10
+ } = options;
11
+
12
+ const paramsArray = [];
13
+
14
+ if (query) {
15
+ paramsArray.push(...(qindex || searchKey).split(',')?.map(m => `match=${m}`));
16
+ paramsArray.push(`term=${query}`);
17
+ }
18
+
19
+ if (filters) {
20
+ const filterMap = {};
21
+ filters.split(',').forEach(filter => {
22
+ const [filterName, ...filterRest] = filter.split('.');
23
+ const filterValue = filterRest.join('.');
24
+
25
+ if (filterMap[filterName] === undefined) filterMap[filterName] = [];
26
+ filterMap[filterName].push(filterValue);
27
+ });
28
+
29
+ // We now have a filterMap of shape { status: ['active', 'cancelled'], type: ['local'] }
30
+ Object.entries(filterMap).forEach(([filterName, filterValues]) => {
31
+ const filterKey = filterKeys[filterName];
32
+
33
+ if (!filterKey) {
34
+ // These filters have no key mapping so we just pass the values to the backend as-is.
35
+ paramsArray.push(...filterValues?.map(f => `filters=${f}`));
36
+ } else {
37
+ const filterString = filterValues.map(v => `${filterKey}==${v}`).join('||');
38
+ paramsArray.push(`filters=${filterString}`);
39
+ }
40
+ });
41
+ }
42
+
43
+ if (sort) {
44
+ paramsArray.push(...sort.split(',').map(sortKey => {
45
+ const descending = sortKey.startsWith('-');
46
+ let term = sortKey.replace('-', '');
47
+
48
+ if (term in sortKeys) {
49
+ term = term.replace(term, sortKeys[term]);
50
+ }
51
+
52
+ return `sort=${term};${descending ? 'desc' : 'asc'}`;
53
+ }));
54
+ }
55
+
56
+ if (stats) {
57
+ paramsArray.push('stats=true');
58
+ }
59
+
60
+ for (const [key, value] of Object.entries(rest)) {
61
+ paramsArray.push(`${key}=${value}`);
62
+ }
63
+
64
+ return paramsArray;
65
+ };
66
+
67
+ export default generateKiwtQueryParams;
@@ -1,2 +1,5 @@
1
1
  export { default as generateKiwtQuery } from './generateKiwtQuery';
2
+ export { default as generateKiwtQueryParams } from './generateKiwtQueryParams';
3
+ export { default as selectorSafe } from './selectorSafe';
4
+
2
5
  export { default as buildUrl } from './buildUrl';
@@ -1,5 +1,5 @@
1
1
  const selectorSafe = (string) => {
2
- return string.replaceAll(/[^a-zA-Z0-9\-_]/, '_');
2
+ return string.replaceAll(/[^a-zA-Z0-9\-_]/g, '_');
3
3
  };
4
4
 
5
5
  export default selectorSafe;
@@ -0,0 +1,38 @@
1
+ /**
2
+ * No Results Message
3
+ */
4
+
5
+ @import '@folio/stripes-components/lib/variables.css';
6
+
7
+ /* Empty Message */
8
+ .noResultsMessage {
9
+ padding: 15px;
10
+ color: var(--color-text-p2);
11
+ position: absolute;
12
+ top: 0;
13
+ bottom: 0;
14
+ left: 0;
15
+ right: 0;
16
+ background: var(--color-fill);
17
+ flex-direction: column;
18
+ }
19
+
20
+ .noResultsMessage,
21
+ .noResultsMessageLabelWrap {
22
+ display: flex;
23
+ align-items: center;
24
+ justify-content: center;
25
+ }
26
+
27
+ .noResultsMessageLabelWrap {
28
+ font-size: var(--font-size-large);
29
+ }
30
+
31
+ .noResultsMessageIcon,
32
+ .noResultsMessageLabel {
33
+ margin: 0 4px;
34
+ }
35
+
36
+ .noResultsMessageButton {
37
+ margin-top: 15px;
38
+ }
@@ -13,5 +13,9 @@
13
13
  "editableRefdataList.edit": "Edit",
14
14
  "editableRefdataList.delete": "Delete",
15
15
  "editableRefdataList.label": "Label",
16
- "editableRefdataList.value": "Value"
16
+ "editableRefdataList.value": "Value",
17
+
18
+ "sasqLookupComponent.mainPane.found": "{total} records found",
19
+
20
+ "noResultsMessage.showFilters": "Show filters"
17
21
  }