@dhis2-ui/transfer 10.10.2 → 10.12.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/build/cjs/__e2e__/notify_at_end_of_list.e2e.stories.js +46 -3
- package/build/cjs/end-intersection-detector.js +5 -4
- package/build/cjs/features/notify_at_end_of_list/index.js +35 -0
- package/build/cjs/features/notify_at_end_of_list.feature +35 -0
- package/build/cjs/options-container.js +14 -8
- package/build/cjs/transfer/index.js +11 -0
- package/build/cjs/transfer/use-options-key-monitor.js +42 -0
- package/build/cjs/transfer.js +41 -14
- package/build/cjs/transfer.prod.stories.js +25 -37
- package/build/es/__e2e__/notify_at_end_of_list.e2e.stories.js +42 -1
- package/build/es/end-intersection-detector.js +4 -3
- package/build/es/features/notify_at_end_of_list/index.js +35 -0
- package/build/es/features/notify_at_end_of_list.feature +35 -0
- package/build/es/options-container.js +14 -8
- package/build/es/transfer/index.js +2 -1
- package/build/es/transfer/use-options-key-monitor.js +35 -0
- package/build/es/transfer.js +39 -14
- package/build/es/transfer.prod.stories.js +26 -38
- package/package.json +7 -7
- package/types/index.d.ts +6 -0
- package/build/cjs/picked-options.js +0 -44
- package/build/cjs/source-options.js +0 -44
- package/build/cjs/use-resize-counter.js +0 -36
- package/build/es/picked-options.js +0 -36
- package/build/es/source-options.js +0 -36
- package/build/es/use-resize-counter.js +0 -30
|
@@ -3,12 +3,13 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.default = exports.PartialSourceList = exports.PartialPickedList = exports.FullSourceList = exports.FullPickedList = void 0;
|
|
7
|
-
var _react =
|
|
6
|
+
exports.default = exports.PartialSourceList = exports.PartialPickedList = exports.OptionChangesForShortList = exports.FullSourceList = exports.FullPickedList = void 0;
|
|
7
|
+
var _react = _interopRequireWildcard(require("react"));
|
|
8
8
|
var _transfer = require("../transfer.js");
|
|
9
9
|
var _options = require("./common/options.js");
|
|
10
10
|
var _statefulDecorator = require("./common/stateful-decorator.js");
|
|
11
|
-
function
|
|
11
|
+
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
12
|
+
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
|
12
13
|
var _default = exports.default = {
|
|
13
14
|
title: 'Transfer End Of List',
|
|
14
15
|
decorators: [(0, _statefulDecorator.statefulDecorator)()]
|
|
@@ -86,4 +87,46 @@ PartialPickedList.story = {
|
|
|
86
87
|
return value;
|
|
87
88
|
})
|
|
88
89
|
})]
|
|
90
|
+
};
|
|
91
|
+
const selectedOptionsLookup = {
|
|
92
|
+
'val-9': {
|
|
93
|
+
value: 'val-9',
|
|
94
|
+
label: 'Option nr. 9'
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
const OptionChangesForShortList = (_, _ref7) => {
|
|
98
|
+
let {
|
|
99
|
+
onChange,
|
|
100
|
+
selected
|
|
101
|
+
} = _ref7;
|
|
102
|
+
const [optionsCount, setOptionsCount] = (0, _react.useState)(7);
|
|
103
|
+
const myOptions = (0, _react.useMemo)(() => {
|
|
104
|
+
let counter = 1;
|
|
105
|
+
const newOptions = [];
|
|
106
|
+
while (counter <= optionsCount) {
|
|
107
|
+
newOptions.push({
|
|
108
|
+
value: `val-${counter}`,
|
|
109
|
+
label: `Option nr. ${counter}`
|
|
110
|
+
});
|
|
111
|
+
counter++;
|
|
112
|
+
}
|
|
113
|
+
return newOptions;
|
|
114
|
+
}, [optionsCount]);
|
|
115
|
+
return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement("button", {
|
|
116
|
+
onClick: () => setOptionsCount(curr => curr + 1)
|
|
117
|
+
}, "Increment options lists"), /*#__PURE__*/_react.default.createElement(_transfer.Transfer, {
|
|
118
|
+
filterable: true,
|
|
119
|
+
selected: selected,
|
|
120
|
+
selectedOptionsLookup: selectedOptionsLookup,
|
|
121
|
+
onChange: onChange,
|
|
122
|
+
options: myOptions,
|
|
123
|
+
onEndReached: window.onEndReached,
|
|
124
|
+
onEndReachedPicked: window.onEndReachedPicked
|
|
125
|
+
}));
|
|
126
|
+
};
|
|
127
|
+
exports.OptionChangesForShortList = OptionChangesForShortList;
|
|
128
|
+
OptionChangesForShortList.story = {
|
|
129
|
+
decorators: [(0, _statefulDecorator.statefulDecorator)({
|
|
130
|
+
initialState: ['val-9']
|
|
131
|
+
})]
|
|
89
132
|
};
|
|
@@ -3,12 +3,13 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.EndIntersectionDetector = void 0;
|
|
6
|
+
exports.INTERSECTION_DETECTOR_HEIGHT = exports.EndIntersectionDetector = void 0;
|
|
7
7
|
var _style = _interopRequireDefault(require("styled-jsx/style"));
|
|
8
8
|
var _intersectionDetector = require("@dhis2-ui/intersection-detector");
|
|
9
9
|
var _propTypes = _interopRequireDefault(require("prop-types"));
|
|
10
10
|
var _react = _interopRequireDefault(require("react"));
|
|
11
11
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
12
|
+
const INTERSECTION_DETECTOR_HEIGHT = exports.INTERSECTION_DETECTOR_HEIGHT = 50;
|
|
12
13
|
const EndIntersectionDetector = _ref => {
|
|
13
14
|
let {
|
|
14
15
|
rootRef,
|
|
@@ -17,7 +18,7 @@ const EndIntersectionDetector = _ref => {
|
|
|
17
18
|
} = _ref;
|
|
18
19
|
return /*#__PURE__*/_react.default.createElement("div", {
|
|
19
20
|
"data-test": dataTest,
|
|
20
|
-
className: "jsx-
|
|
21
|
+
className: "jsx-3586508456"
|
|
21
22
|
}, /*#__PURE__*/_react.default.createElement(_intersectionDetector.IntersectionDetector, {
|
|
22
23
|
rootRef: rootRef,
|
|
23
24
|
onChange: _ref2 => {
|
|
@@ -27,8 +28,8 @@ const EndIntersectionDetector = _ref => {
|
|
|
27
28
|
return isIntersecting && onEndReached();
|
|
28
29
|
}
|
|
29
30
|
}), /*#__PURE__*/_react.default.createElement(_style.default, {
|
|
30
|
-
id: "
|
|
31
|
-
}, [
|
|
31
|
+
id: "3586508456"
|
|
32
|
+
}, [`div.jsx-3586508456{width:100%;height:${INTERSECTION_DETECTOR_HEIGHT}px;position:absolute;z-index:-1;bottom:0;inset-inline-start:0;}`]));
|
|
32
33
|
};
|
|
33
34
|
exports.EndIntersectionDetector = EndIntersectionDetector;
|
|
34
35
|
EndIntersectionDetector.propTypes = {
|
|
@@ -13,6 +13,10 @@ var _cypressCucumberPreprocessor = require("@badeball/cypress-cucumber-preproces
|
|
|
13
13
|
cy.visitStory('Transfer End Of List', 'Partial Source List');
|
|
14
14
|
cy.wrap('source').as('listType');
|
|
15
15
|
});
|
|
16
|
+
(0, _cypressCucumberPreprocessor.Given)('the Transfer source options list does not fill the list completely', () => {
|
|
17
|
+
cy.visitStory('Transfer End Of List', 'Option Changes For Short List');
|
|
18
|
+
cy.wrap('source').as('listType');
|
|
19
|
+
});
|
|
16
20
|
(0, _cypressCucumberPreprocessor.Given)('the Transfer does not have enough items to fill the picked list completely', () => {
|
|
17
21
|
cy.visitStory('Transfer End Of List', 'Partial Picked List');
|
|
18
22
|
cy.wrap('picked').as('listType');
|
|
@@ -23,6 +27,12 @@ var _cypressCucumberPreprocessor = require("@badeball/cypress-cucumber-preproces
|
|
|
23
27
|
cy.get(`{${listSelector}-endintersectiondetector}`).scrollIntoView();
|
|
24
28
|
});
|
|
25
29
|
});
|
|
30
|
+
(0, _cypressCucumberPreprocessor.When)('the user adds an item by clicking the button', () => {
|
|
31
|
+
cy.contains('Increment options lists').click();
|
|
32
|
+
});
|
|
33
|
+
(0, _cypressCucumberPreprocessor.Then)('the last list item should be fully visible', () => {
|
|
34
|
+
cy.contains('ARI treated with antibiotics (pneumonia) new').should('be.visible');
|
|
35
|
+
});
|
|
26
36
|
(0, _cypressCucumberPreprocessor.Then)('the callback for reaching the end should not be called', () => {
|
|
27
37
|
cy.all(() => cy.window(), () => cy.get('@listType')).should(_ref => {
|
|
28
38
|
let [win, listType] = _ref;
|
|
@@ -36,4 +46,29 @@ var _cypressCucumberPreprocessor = require("@badeball/cypress-cucumber-preproces
|
|
|
36
46
|
const callback = listType === 'source' ? win.onEndReached : win.onEndReachedPicked;
|
|
37
47
|
expect(callback).to.be.calledOnce;
|
|
38
48
|
});
|
|
49
|
+
});
|
|
50
|
+
(0, _cypressCucumberPreprocessor.When)('the user scrolls down to the source list end', () => {
|
|
51
|
+
cy.get('[data-test="dhis2-uicore-transfer-sourceoptions"]').find('[data-test="dhis2-uicore-intersectiondetector"]').scrollIntoView();
|
|
52
|
+
});
|
|
53
|
+
(0, _cypressCucumberPreprocessor.Then)('the list end indicator of the source list should be visible', () => {
|
|
54
|
+
cy.get('[data-test="dhis2-uicore-transfer-sourceoptions"]').find('[data-test="dhis2-uicore-intersectiondetector"]').should('be.visible');
|
|
55
|
+
});
|
|
56
|
+
(0, _cypressCucumberPreprocessor.Then)('the list end indicator of the source list should not be visible', () => {
|
|
57
|
+
cy.get('[data-test="dhis2-uicore-transfer-sourceoptions"]').find('[data-test="dhis2-uicore-intersectiondetector"]').should('not.be.visible');
|
|
58
|
+
});
|
|
59
|
+
(0, _cypressCucumberPreprocessor.Then)('the callback for reaching the end of the source list should be called {int} times', function (int) {
|
|
60
|
+
cy.window().should(win => {
|
|
61
|
+
expect(win.onEndReached).to.have.callCount(int);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
(0, _cypressCucumberPreprocessor.Then)('the selected item is being displayed in the picked list', () => {
|
|
65
|
+
cy.get('[data-test="dhis2-uicore-transfer-pickedoptions"]').contains('Option nr. 9').should('be.visible');
|
|
66
|
+
});
|
|
67
|
+
(0, _cypressCucumberPreprocessor.When)('the user selects option nr. {}', function (int) {
|
|
68
|
+
cy.contains(`Option nr. ${int}`).dblclick();
|
|
69
|
+
});
|
|
70
|
+
(0, _cypressCucumberPreprocessor.Then)('the callback for reaching the end of the picked list should be called {int} times', function (int) {
|
|
71
|
+
cy.window().should(win => {
|
|
72
|
+
expect(win.onEndReachedPicked).to.have.callCount(int);
|
|
73
|
+
});
|
|
39
74
|
});
|
|
@@ -27,3 +27,38 @@ Feature: The source and picked option lists notify the consumer when the end has
|
|
|
27
27
|
| type |
|
|
28
28
|
| source |
|
|
29
29
|
| picked |
|
|
30
|
+
|
|
31
|
+
Scenario: The list is short and items are added within the list container
|
|
32
|
+
Given the Transfer source options list does not fill the list completely
|
|
33
|
+
# Initial state: the list has 7 items and the list ends well above the container bottom
|
|
34
|
+
Then the list end indicator of the source list should be visible
|
|
35
|
+
Then the callback for reaching the end of the source list should be called 1 times
|
|
36
|
+
Then the callback for reaching the end of the picked list should be called 1 times
|
|
37
|
+
# Selected item is not in the options array but is present in the `selectedOptionsLookup`
|
|
38
|
+
Then the selected item is being displayed in the picked list
|
|
39
|
+
When the user adds an item by clicking the button
|
|
40
|
+
# The indicator is still just in view
|
|
41
|
+
Then the list end indicator of the source list should be visible
|
|
42
|
+
Then the callback for reaching the end of the source list should be called 2 times
|
|
43
|
+
When the user adds an item by clicking the button
|
|
44
|
+
# This adds val-9, which is a selected item so it gets added to picked options
|
|
45
|
+
# not source options, but the callback is still called
|
|
46
|
+
Then the list end indicator of the source list should be visible
|
|
47
|
+
Then the callback for reaching the end of the source list should be called 3 times
|
|
48
|
+
# The picked list callback does not get called because the picked item was already present
|
|
49
|
+
Then the callback for reaching the end of the picked list should be called 1 times
|
|
50
|
+
When the user adds an item by clicking the button
|
|
51
|
+
# The indicator is still in view but only just
|
|
52
|
+
Then the list end indicator of the source list should be visible
|
|
53
|
+
Then the callback for reaching the end of the source list should be called 4 times
|
|
54
|
+
When the user adds an item by clicking the button
|
|
55
|
+
# The indicator now is out of view, no more calls
|
|
56
|
+
Then the list end indicator of the source list should not be visible
|
|
57
|
+
Then the callback for reaching the end of the source list should be called 4 times
|
|
58
|
+
# But scrolling down does trigger a call
|
|
59
|
+
When the user scrolls down to the source list end
|
|
60
|
+
Then the list end indicator of the source list should be visible
|
|
61
|
+
Then the callback for reaching the end of the source list should be called 5 times
|
|
62
|
+
# And selecting an item triggers a call in the picked list
|
|
63
|
+
When the user selects option nr. 11
|
|
64
|
+
Then the callback for reaching the end of the picked list should be called 2 times
|
|
@@ -9,12 +9,13 @@ var _loader = require("@dhis2-ui/loader");
|
|
|
9
9
|
var _propTypes = _interopRequireDefault(require("prop-types"));
|
|
10
10
|
var _react = _interopRequireWildcard(require("react"));
|
|
11
11
|
var _endIntersectionDetector = require("./end-intersection-detector.js");
|
|
12
|
-
var
|
|
12
|
+
var _useOptionsKeyMonitor = require("./transfer/use-options-key-monitor.js");
|
|
13
13
|
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
14
14
|
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
|
15
15
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
16
16
|
const OptionsContainer = _ref => {
|
|
17
17
|
let {
|
|
18
|
+
allOptionsKey,
|
|
18
19
|
dataTest,
|
|
19
20
|
emptyComponent,
|
|
20
21
|
onEndReached,
|
|
@@ -27,19 +28,24 @@ const OptionsContainer = _ref => {
|
|
|
27
28
|
selectionHandler,
|
|
28
29
|
toggleHighlightedOption
|
|
29
30
|
} = _ref;
|
|
30
|
-
const
|
|
31
|
-
const
|
|
32
|
-
|
|
31
|
+
const scrollBoxRef = (0, _react.useRef)(null);
|
|
32
|
+
const listRef = (0, _react.useRef)(null);
|
|
33
|
+
(0, _useOptionsKeyMonitor.useOptionsKeyMonitor)({
|
|
34
|
+
scrollBoxRef,
|
|
35
|
+
listRef,
|
|
36
|
+
allOptionsKey,
|
|
37
|
+
onEndReached
|
|
38
|
+
});
|
|
33
39
|
return /*#__PURE__*/_react.default.createElement("div", {
|
|
34
40
|
className: "jsx-1882699425" + " " + "optionsContainer"
|
|
35
41
|
}, loading && /*#__PURE__*/_react.default.createElement("div", {
|
|
36
42
|
className: "jsx-1882699425" + " " + "loading"
|
|
37
43
|
}, /*#__PURE__*/_react.default.createElement(_loader.CircularLoader, null)), /*#__PURE__*/_react.default.createElement("div", {
|
|
38
44
|
"data-test": dataTest,
|
|
39
|
-
ref:
|
|
45
|
+
ref: scrollBoxRef,
|
|
40
46
|
className: "jsx-1882699425" + " " + "container"
|
|
41
47
|
}, /*#__PURE__*/_react.default.createElement("div", {
|
|
42
|
-
ref:
|
|
48
|
+
ref: listRef,
|
|
43
49
|
className: "jsx-1882699425" + " " + "content-container"
|
|
44
50
|
}, !options.length && emptyComponent, options.map(option => {
|
|
45
51
|
const highlighted = !!highlightedOptions.find(highlightedSourceOption => highlightedSourceOption === option.value);
|
|
@@ -53,8 +59,7 @@ const OptionsContainer = _ref => {
|
|
|
53
59
|
}));
|
|
54
60
|
}), onEndReached && /*#__PURE__*/_react.default.createElement(_endIntersectionDetector.EndIntersectionDetector, {
|
|
55
61
|
dataTest: `${dataTest}-endintersectiondetector`,
|
|
56
|
-
|
|
57
|
-
rootRef: optionsRef,
|
|
62
|
+
rootRef: scrollBoxRef,
|
|
58
63
|
onEndReached: onEndReached
|
|
59
64
|
}))), /*#__PURE__*/_react.default.createElement(_style.default, {
|
|
60
65
|
id: "1882699425"
|
|
@@ -62,6 +67,7 @@ const OptionsContainer = _ref => {
|
|
|
62
67
|
};
|
|
63
68
|
exports.OptionsContainer = OptionsContainer;
|
|
64
69
|
OptionsContainer.propTypes = {
|
|
70
|
+
allOptionsKey: _propTypes.default.string.isRequired,
|
|
65
71
|
dataTest: _propTypes.default.string.isRequired,
|
|
66
72
|
getOptionClickHandlers: _propTypes.default.func.isRequired,
|
|
67
73
|
emptyComponent: _propTypes.default.node,
|
|
@@ -145,4 +145,15 @@ Object.keys(_useHighlightedOptions).forEach(function (key) {
|
|
|
145
145
|
return _useHighlightedOptions[key];
|
|
146
146
|
}
|
|
147
147
|
});
|
|
148
|
+
});
|
|
149
|
+
var _useOptionsKeyMonitor = require("./use-options-key-monitor.js");
|
|
150
|
+
Object.keys(_useOptionsKeyMonitor).forEach(function (key) {
|
|
151
|
+
if (key === "default" || key === "__esModule") return;
|
|
152
|
+
if (key in exports && exports[key] === _useOptionsKeyMonitor[key]) return;
|
|
153
|
+
Object.defineProperty(exports, key, {
|
|
154
|
+
enumerable: true,
|
|
155
|
+
get: function () {
|
|
156
|
+
return _useOptionsKeyMonitor[key];
|
|
157
|
+
}
|
|
158
|
+
});
|
|
148
159
|
});
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.useOptionsKeyMonitor = void 0;
|
|
7
|
+
var _react = require("react");
|
|
8
|
+
var _endIntersectionDetector = require("../end-intersection-detector.js");
|
|
9
|
+
const isEndIntersectionDetectorWithinScrollBox = (scrollBoxRef, listRef) => {
|
|
10
|
+
if (!scrollBoxRef.current || !listRef.current) {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
const scrollBoxRect = scrollBoxRef.current.getBoundingClientRect();
|
|
14
|
+
const listRect = listRef.current.getBoundingClientRect();
|
|
15
|
+
return listRect.bottom - scrollBoxRect.bottom < _endIntersectionDetector.INTERSECTION_DETECTOR_HEIGHT;
|
|
16
|
+
};
|
|
17
|
+
const useOptionsKeyMonitor = _ref => {
|
|
18
|
+
let {
|
|
19
|
+
scrollBoxRef,
|
|
20
|
+
listRef,
|
|
21
|
+
allOptionsKey,
|
|
22
|
+
onEndReached
|
|
23
|
+
} = _ref;
|
|
24
|
+
/* Store in ref so this works even if a consumer does not pass a stable
|
|
25
|
+
* function reference */
|
|
26
|
+
const onEndReachedRef = (0, _react.useRef)(onEndReached);
|
|
27
|
+
const prevAllOptionsKey = (0, _react.useRef)(allOptionsKey);
|
|
28
|
+
onEndReachedRef.current = onEndReached;
|
|
29
|
+
(0, _react.useEffect)(() => {
|
|
30
|
+
/* When new options are loaded and the list end is (still) in
|
|
31
|
+
* view we need to call onEndReached, because the end of the list
|
|
32
|
+
* has indeed been reached but the interception detector will not pick
|
|
33
|
+
* up on this */
|
|
34
|
+
if (onEndReachedRef.current && prevAllOptionsKey.current !== allOptionsKey && isEndIntersectionDetectorWithinScrollBox(scrollBoxRef, listRef)) {
|
|
35
|
+
onEndReachedRef.current();
|
|
36
|
+
}
|
|
37
|
+
prevAllOptionsKey.current = allOptionsKey;
|
|
38
|
+
/* This effect will only run on mount and when allOptionsKey
|
|
39
|
+
* changes because scrollBoxRef, listRef are stable references */
|
|
40
|
+
}, [scrollBoxRef, listRef, allOptionsKey]);
|
|
41
|
+
};
|
|
42
|
+
exports.useOptionsKeyMonitor = useOptionsKeyMonitor;
|
package/build/cjs/transfer.js
CHANGED
|
@@ -5,7 +5,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.Transfer = void 0;
|
|
7
7
|
var _propTypes = _interopRequireDefault(require("prop-types"));
|
|
8
|
-
var _react =
|
|
8
|
+
var _react = _interopRequireWildcard(require("react"));
|
|
9
9
|
var _actions = require("./actions.js");
|
|
10
10
|
var _addAll = require("./add-all.js");
|
|
11
11
|
var _addIndividual = require("./add-individual.js");
|
|
@@ -23,9 +23,12 @@ var _rightHeader = require("./right-header.js");
|
|
|
23
23
|
var _rightSide = require("./right-side.js");
|
|
24
24
|
var _index = require("./transfer/index.js");
|
|
25
25
|
var _transferOption = require("./transfer-option.js");
|
|
26
|
+
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
27
|
+
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
|
26
28
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
27
29
|
const identity = value => value;
|
|
28
30
|
const defaultSelected = [];
|
|
31
|
+
const defaultSelectedOptionsLookup = {};
|
|
29
32
|
const Transfer = _ref => {
|
|
30
33
|
let {
|
|
31
34
|
options,
|
|
@@ -49,6 +52,7 @@ const Transfer = _ref => {
|
|
|
49
52
|
hideFilterInputPicked,
|
|
50
53
|
initialSearchTerm = '',
|
|
51
54
|
initialSearchTermPicked = '',
|
|
55
|
+
selectedOptionsLookup = defaultSelectedOptionsLookup,
|
|
52
56
|
leftFooter,
|
|
53
57
|
leftHeader,
|
|
54
58
|
loadingPicked,
|
|
@@ -131,16 +135,15 @@ const Transfer = _ref => {
|
|
|
131
135
|
* Actual picked options:
|
|
132
136
|
* Extract the selected options. Can't use `options.filter`
|
|
133
137
|
* because we need to keep the order of `selected`
|
|
138
|
+
* Note: Only map if selected is an array
|
|
134
139
|
*/
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
.filter(identity), actualFilterPicked);
|
|
143
|
-
}
|
|
140
|
+
const pickedOptions = (0, _react.useMemo)(() => Array.isArray(selected) ? actualFilterPickedCallback(selected.map(value => {
|
|
141
|
+
var _selectedOptionsLooku;
|
|
142
|
+
return (_selectedOptionsLooku = selectedOptionsLookup[value]) !== null && _selectedOptionsLooku !== void 0 ? _selectedOptionsLooku : options.find(option => value === option.value);
|
|
143
|
+
})
|
|
144
|
+
// filter -> in case a selected value has been provided
|
|
145
|
+
// that does not exist as option
|
|
146
|
+
.filter(identity), actualFilterPicked) : [], [selected, options, actualFilterPicked, actualFilterPickedCallback, selectedOptionsLookup]);
|
|
144
147
|
|
|
145
148
|
/*
|
|
146
149
|
* Source options highlighting:
|
|
@@ -183,6 +186,18 @@ const Transfer = _ref => {
|
|
|
183
186
|
const isAddIndividualDisabled = disabled || !highlightedSourceOptions.length;
|
|
184
187
|
const isRemoveAllDisabled = disabled || !selected.length;
|
|
185
188
|
const isRemoveIndividualDisabled = disabled || !highlightedPickedOptions.length;
|
|
189
|
+
const allOptionsKey = (0, _react.useMemo)(() => options.map(_ref4 => {
|
|
190
|
+
let {
|
|
191
|
+
value
|
|
192
|
+
} = _ref4;
|
|
193
|
+
return value;
|
|
194
|
+
}).join('|'), [options]);
|
|
195
|
+
const pickedOptionsKey = (0, _react.useMemo)(() => pickedOptions.map(_ref5 => {
|
|
196
|
+
let {
|
|
197
|
+
value
|
|
198
|
+
} = _ref5;
|
|
199
|
+
return value;
|
|
200
|
+
}).join('|'), [pickedOptions]);
|
|
186
201
|
return /*#__PURE__*/_react.default.createElement(_container.Container, {
|
|
187
202
|
dataTest: dataTest,
|
|
188
203
|
className: className,
|
|
@@ -197,13 +212,14 @@ const Transfer = _ref => {
|
|
|
197
212
|
placeholder: filterPlaceholder,
|
|
198
213
|
dataTest: `${dataTest}-filter`,
|
|
199
214
|
filter: actualFilter,
|
|
200
|
-
onChange: onFilterChange ? onFilterChange :
|
|
215
|
+
onChange: onFilterChange ? onFilterChange : _ref6 => {
|
|
201
216
|
let {
|
|
202
217
|
value
|
|
203
|
-
} =
|
|
218
|
+
} = _ref6;
|
|
204
219
|
return setInternalFilter(value);
|
|
205
220
|
}
|
|
206
221
|
})), /*#__PURE__*/_react.default.createElement(_optionsContainer.OptionsContainer, {
|
|
222
|
+
allOptionsKey: allOptionsKey,
|
|
207
223
|
dataTest: `${dataTest}-sourceoptions`,
|
|
208
224
|
emptyComponent: sourceEmptyPlaceholder,
|
|
209
225
|
getOptionClickHandlers: _index.getOptionClickHandlers,
|
|
@@ -271,14 +287,15 @@ const Transfer = _ref => {
|
|
|
271
287
|
placeholder: filterPlaceholderPicked,
|
|
272
288
|
dataTest: `${dataTest}-filter`,
|
|
273
289
|
filter: actualFilterPicked,
|
|
274
|
-
onChange: onFilterChangePicked ? onFilterChangePicked :
|
|
290
|
+
onChange: onFilterChangePicked ? onFilterChangePicked : _ref7 => {
|
|
275
291
|
let {
|
|
276
292
|
value
|
|
277
|
-
} =
|
|
293
|
+
} = _ref7;
|
|
278
294
|
return setInternalFilterPicked(value);
|
|
279
295
|
}
|
|
280
296
|
})), /*#__PURE__*/_react.default.createElement(_optionsContainer.OptionsContainer, {
|
|
281
297
|
selected: true,
|
|
298
|
+
allOptionsKey: pickedOptionsKey,
|
|
282
299
|
dataTest: `${dataTest}-pickedoptions`,
|
|
283
300
|
emptyComponent: selectedEmptyComponent,
|
|
284
301
|
getOptionClickHandlers: _index.getOptionClickHandlers,
|
|
@@ -358,6 +375,16 @@ Transfer.propTypes = {
|
|
|
358
375
|
searchTermPicked: _propTypes.default.string,
|
|
359
376
|
selected: _propTypes.default.arrayOf(_propTypes.default.string),
|
|
360
377
|
selectedEmptyComponent: _propTypes.default.node,
|
|
378
|
+
/**
|
|
379
|
+
* To be used in scenarios where selected options may not be present
|
|
380
|
+
* in the options array. Like when having options that lazy load or can
|
|
381
|
+
* be filtered async.
|
|
382
|
+
*/
|
|
383
|
+
selectedOptionsLookup: _propTypes.default.objectOf(_propTypes.default.shape({
|
|
384
|
+
label: _propTypes.default.string.isRequired,
|
|
385
|
+
value: _propTypes.default.string.isRequired,
|
|
386
|
+
disabled: _propTypes.default.bool
|
|
387
|
+
})),
|
|
361
388
|
selectedWidth: _propTypes.default.string,
|
|
362
389
|
sourceEmptyPlaceholder: _propTypes.default.node,
|
|
363
390
|
onEndReached: _propTypes.default.func,
|
|
@@ -454,6 +454,8 @@ const pageSize = 5;
|
|
|
454
454
|
* To keep the code as small as possible, handling selecting items is not
|
|
455
455
|
included
|
|
456
456
|
*/
|
|
457
|
+
const preSelectedOptions = optionsPool.slice(optionsPool.length - 4);
|
|
458
|
+
const waitMs = async ms => new Promise(resolve => setTimeout(resolve, ms));
|
|
457
459
|
const InfiniteLoading = args => {
|
|
458
460
|
(0, _react.useEffect)(() => {
|
|
459
461
|
console.clear();
|
|
@@ -461,59 +463,45 @@ const InfiniteLoading = args => {
|
|
|
461
463
|
|
|
462
464
|
// state for whether the next page's options are being loaded
|
|
463
465
|
const [loading, setLoading] = (0, _react.useState)(false);
|
|
464
|
-
//
|
|
465
|
-
const [
|
|
466
|
-
// all options (incl. available AND selected options)
|
|
466
|
+
// prevent fetches after list is complete
|
|
467
|
+
const [isOptionsListComplete, setIsOptionsListComplete] = (0, _react.useState)(false);
|
|
467
468
|
const [options, setOptions] = (0, _react.useState)([]);
|
|
468
469
|
// selected options
|
|
469
470
|
const [selected] = (0, _react.useState)(
|
|
470
|
-
//
|
|
471
|
-
|
|
471
|
+
// Last 4 options preselected
|
|
472
|
+
preSelectedOptions.map(_ref13 => {
|
|
472
473
|
let {
|
|
473
474
|
value
|
|
474
475
|
} = _ref13;
|
|
475
476
|
return value;
|
|
476
477
|
}));
|
|
477
|
-
const
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
setPage(page + 1);
|
|
483
|
-
};
|
|
484
|
-
|
|
485
|
-
// fake fetch request
|
|
486
|
-
const fetchOptions = nextPage => new Promise(resolve => setTimeout(() => {
|
|
487
|
-
const nextOptions = optionsPool.slice(options.length, nextPage * pageSize);
|
|
488
|
-
resolve(nextOptions);
|
|
489
|
-
}, 2000));
|
|
490
|
-
const loadNextOptions = async () => {
|
|
478
|
+
const selectedOptionsLookup = (0, _react.useMemo)(() => preSelectedOptions.reduce((lookup, option) => {
|
|
479
|
+
lookup[option.value] = option;
|
|
480
|
+
return lookup;
|
|
481
|
+
}, {}), []);
|
|
482
|
+
const loadMoreOptions = (0, _react.useCallback)(async () => {
|
|
491
483
|
setLoading(true);
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
} = nextOption;
|
|
499
|
-
return selected.includes(value);
|
|
500
|
-
});
|
|
501
|
-
if (allAlreadySelected) {
|
|
502
|
-
onEndReached();
|
|
484
|
+
await waitMs(2000);
|
|
485
|
+
const newOptions = optionsPool.slice(options.length, Math.min(options.length + pageSize, optionsPool.length));
|
|
486
|
+
const combinedOptions = [...options, ...newOptions];
|
|
487
|
+
setOptions(combinedOptions);
|
|
488
|
+
if (combinedOptions.length === optionsPool.length) {
|
|
489
|
+
setIsOptionsListComplete(true);
|
|
503
490
|
}
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
if (
|
|
508
|
-
|
|
491
|
+
setLoading(false);
|
|
492
|
+
}, [options]);
|
|
493
|
+
const onEndReached = (0, _react.useCallback)(() => {
|
|
494
|
+
if (!isOptionsListComplete) {
|
|
495
|
+
loadMoreOptions();
|
|
509
496
|
}
|
|
510
|
-
}, [
|
|
497
|
+
}, [loadMoreOptions, isOptionsListComplete]);
|
|
511
498
|
return /*#__PURE__*/_react.default.createElement(_transfer.Transfer, _extends({}, args, {
|
|
512
499
|
loading: loading,
|
|
513
500
|
options: options,
|
|
514
501
|
selected: selected,
|
|
515
502
|
onChange: () => null /* noop */,
|
|
516
|
-
onEndReached: onEndReached
|
|
503
|
+
onEndReached: onEndReached,
|
|
504
|
+
selectedOptionsLookup: selectedOptionsLookup
|
|
517
505
|
}));
|
|
518
506
|
};
|
|
519
507
|
exports.InfiniteLoading = InfiniteLoading;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { useMemo, useState } from 'react';
|
|
2
2
|
import { Transfer } from '../transfer.js';
|
|
3
3
|
import { options } from './common/options.js';
|
|
4
4
|
import { statefulDecorator } from './common/stateful-decorator.js';
|
|
@@ -75,4 +75,45 @@ PartialPickedList.story = {
|
|
|
75
75
|
return value;
|
|
76
76
|
})
|
|
77
77
|
})]
|
|
78
|
+
};
|
|
79
|
+
const selectedOptionsLookup = {
|
|
80
|
+
'val-9': {
|
|
81
|
+
value: 'val-9',
|
|
82
|
+
label: 'Option nr. 9'
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
export const OptionChangesForShortList = (_, _ref7) => {
|
|
86
|
+
let {
|
|
87
|
+
onChange,
|
|
88
|
+
selected
|
|
89
|
+
} = _ref7;
|
|
90
|
+
const [optionsCount, setOptionsCount] = useState(7);
|
|
91
|
+
const myOptions = useMemo(() => {
|
|
92
|
+
let counter = 1;
|
|
93
|
+
const newOptions = [];
|
|
94
|
+
while (counter <= optionsCount) {
|
|
95
|
+
newOptions.push({
|
|
96
|
+
value: `val-${counter}`,
|
|
97
|
+
label: `Option nr. ${counter}`
|
|
98
|
+
});
|
|
99
|
+
counter++;
|
|
100
|
+
}
|
|
101
|
+
return newOptions;
|
|
102
|
+
}, [optionsCount]);
|
|
103
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("button", {
|
|
104
|
+
onClick: () => setOptionsCount(curr => curr + 1)
|
|
105
|
+
}, "Increment options lists"), /*#__PURE__*/React.createElement(Transfer, {
|
|
106
|
+
filterable: true,
|
|
107
|
+
selected: selected,
|
|
108
|
+
selectedOptionsLookup: selectedOptionsLookup,
|
|
109
|
+
onChange: onChange,
|
|
110
|
+
options: myOptions,
|
|
111
|
+
onEndReached: window.onEndReached,
|
|
112
|
+
onEndReachedPicked: window.onEndReachedPicked
|
|
113
|
+
}));
|
|
114
|
+
};
|
|
115
|
+
OptionChangesForShortList.story = {
|
|
116
|
+
decorators: [statefulDecorator({
|
|
117
|
+
initialState: ['val-9']
|
|
118
|
+
})]
|
|
78
119
|
};
|
|
@@ -2,6 +2,7 @@ import _JSXStyle from "styled-jsx/style";
|
|
|
2
2
|
import { IntersectionDetector } from '@dhis2-ui/intersection-detector';
|
|
3
3
|
import PropTypes from 'prop-types';
|
|
4
4
|
import React from 'react';
|
|
5
|
+
export const INTERSECTION_DETECTOR_HEIGHT = 50;
|
|
5
6
|
export const EndIntersectionDetector = _ref => {
|
|
6
7
|
let {
|
|
7
8
|
rootRef,
|
|
@@ -10,7 +11,7 @@ export const EndIntersectionDetector = _ref => {
|
|
|
10
11
|
} = _ref;
|
|
11
12
|
return /*#__PURE__*/React.createElement("div", {
|
|
12
13
|
"data-test": dataTest,
|
|
13
|
-
className: "jsx-
|
|
14
|
+
className: "jsx-3586508456"
|
|
14
15
|
}, /*#__PURE__*/React.createElement(IntersectionDetector, {
|
|
15
16
|
rootRef: rootRef,
|
|
16
17
|
onChange: _ref2 => {
|
|
@@ -20,8 +21,8 @@ export const EndIntersectionDetector = _ref => {
|
|
|
20
21
|
return isIntersecting && onEndReached();
|
|
21
22
|
}
|
|
22
23
|
}), /*#__PURE__*/React.createElement(_JSXStyle, {
|
|
23
|
-
id: "
|
|
24
|
-
}, [
|
|
24
|
+
id: "3586508456"
|
|
25
|
+
}, [`div.jsx-3586508456{width:100%;height:${INTERSECTION_DETECTOR_HEIGHT}px;position:absolute;z-index:-1;bottom:0;inset-inline-start:0;}`]));
|
|
25
26
|
};
|
|
26
27
|
EndIntersectionDetector.propTypes = {
|
|
27
28
|
rootRef: PropTypes.shape({
|