@contentful/field-editor-tags 1.2.0 → 1.3.1

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.
@@ -0,0 +1,185 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ Object.defineProperty(exports, "TagsEditor", {
6
+ enumerable: true,
7
+ get: function() {
8
+ return TagsEditor;
9
+ }
10
+ });
11
+ const _react = _interop_require_wildcard(require("react"));
12
+ const _reactsortablehoc = require("react-sortable-hoc");
13
+ const _f36components = require("@contentful/f36-components");
14
+ const _f36icons = require("@contentful/f36-icons");
15
+ const _f36tokens = _interop_require_default(require("@contentful/f36-tokens"));
16
+ const _arraymove = _interop_require_default(require("array-move"));
17
+ const _emotion = require("emotion");
18
+ const _noop = _interop_require_default(require("lodash/noop"));
19
+ const _TagsEditorConstraints = require("./TagsEditorConstraints");
20
+ function _interop_require_default(obj) {
21
+ return obj && obj.__esModule ? obj : {
22
+ default: obj
23
+ };
24
+ }
25
+ function _getRequireWildcardCache(nodeInterop) {
26
+ if (typeof WeakMap !== "function") return null;
27
+ var cacheBabelInterop = new WeakMap();
28
+ var cacheNodeInterop = new WeakMap();
29
+ return (_getRequireWildcardCache = function(nodeInterop) {
30
+ return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
31
+ })(nodeInterop);
32
+ }
33
+ function _interop_require_wildcard(obj, nodeInterop) {
34
+ if (!nodeInterop && obj && obj.__esModule) {
35
+ return obj;
36
+ }
37
+ if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
38
+ return {
39
+ default: obj
40
+ };
41
+ }
42
+ var cache = _getRequireWildcardCache(nodeInterop);
43
+ if (cache && cache.has(obj)) {
44
+ return cache.get(obj);
45
+ }
46
+ var newObj = {};
47
+ var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
48
+ for(var key in obj){
49
+ if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
50
+ var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
51
+ if (desc && (desc.get || desc.set)) {
52
+ Object.defineProperty(newObj, key, desc);
53
+ } else {
54
+ newObj[key] = obj[key];
55
+ }
56
+ }
57
+ }
58
+ newObj.default = obj;
59
+ if (cache) {
60
+ cache.set(obj, newObj);
61
+ }
62
+ return newObj;
63
+ }
64
+ const styles = {
65
+ dropContainer: (0, _emotion.css)({
66
+ whiteSpace: 'nowrap',
67
+ display: 'flex',
68
+ flexWrap: 'wrap'
69
+ }),
70
+ input: (0, _emotion.css)({
71
+ marginTop: _f36tokens.default.spacingS,
72
+ marginBottom: _f36tokens.default.spacingM
73
+ }),
74
+ pill: (0, _emotion.css)({
75
+ marginRight: _f36tokens.default.spacingS,
76
+ marginBottom: _f36tokens.default.spacingS
77
+ }),
78
+ pillDisabled: (0, _emotion.css)({
79
+ cursor: 'not-allowed !important',
80
+ button: {
81
+ cursor: 'not-allowed !important',
82
+ pointerEvents: 'none'
83
+ }
84
+ }),
85
+ handle: (0, _emotion.css)({
86
+ lineHeight: '1.5rem',
87
+ padding: '0.375rem 0.625rem',
88
+ paddingRight: 0,
89
+ cursor: 'grab',
90
+ userSelect: 'none',
91
+ svg: {
92
+ fill: _f36tokens.default.gray500,
93
+ verticalAlign: 'middle'
94
+ }
95
+ })
96
+ };
97
+ const SortablePillHandle = (0, _reactsortablehoc.SortableHandle)((props)=>_react.default.createElement("div", {
98
+ className: (0, _emotion.cx)(styles.handle, {
99
+ [styles.pillDisabled]: props.isDisabled
100
+ })
101
+ }, _react.default.createElement(_f36icons.DragIcon, {
102
+ variant: "muted"
103
+ })));
104
+ const SortablePill = (0, _reactsortablehoc.SortableElement)((props)=>_react.default.createElement(_f36components.Pill, {
105
+ testId: "tag-editor-pill",
106
+ className: (0, _emotion.cx)(styles.pill, {
107
+ [styles.pillDisabled]: props.isSortablePillDisabled
108
+ }),
109
+ label: props.label,
110
+ onClose: ()=>{
111
+ if (!props.isSortablePillDisabled) {
112
+ props.onRemove(props.index);
113
+ }
114
+ },
115
+ onDrag: _noop.default,
116
+ dragHandleComponent: _react.default.createElement(SortablePillHandle, {
117
+ isDisabled: props.isSortablePillDisabled
118
+ })
119
+ }));
120
+ const SortableList = (0, _reactsortablehoc.SortableContainer)((props)=>_react.default.createElement("div", {
121
+ className: styles.dropContainer
122
+ }, props.children));
123
+ function TagsEditor(props) {
124
+ const [pendingValue, setPendingValue] = (0, _react.useState)('');
125
+ const { isDisabled , items , constraints , constraintsType , hasError } = props;
126
+ const removeItem = (0, _react.useCallback)((index)=>{
127
+ const newItems = props.items.filter((_, filterIndex)=>index !== filterIndex);
128
+ props.onUpdate(newItems);
129
+ }, [
130
+ props
131
+ ]);
132
+ const swapItems = (0, _react.useCallback)(({ oldIndex , newIndex })=>{
133
+ const newItems = (0, _arraymove.default)(props.items, oldIndex, newIndex);
134
+ props.onUpdate(newItems);
135
+ }, [
136
+ props
137
+ ]);
138
+ return _react.default.createElement("div", {
139
+ "data-test-id": "tag-editor-container"
140
+ }, _react.default.createElement(_f36components.TextInput, {
141
+ testId: "tag-editor-input",
142
+ className: styles.input,
143
+ isDisabled: isDisabled,
144
+ isInvalid: hasError,
145
+ type: "text",
146
+ value: pendingValue,
147
+ placeholder: "Type the value and hit enter",
148
+ onKeyDown: (e)=>{
149
+ if (pendingValue && e.keyCode === 13) {
150
+ props.onUpdate([
151
+ ...props.items,
152
+ pendingValue
153
+ ]);
154
+ setPendingValue('');
155
+ }
156
+ },
157
+ onChange: (e)=>{
158
+ setPendingValue(e.target.value);
159
+ }
160
+ }), _react.default.createElement(SortableList, {
161
+ useDragHandle: true,
162
+ axis: "xy",
163
+ distance: 10,
164
+ onSortEnd: ({ oldIndex , newIndex })=>{
165
+ swapItems({
166
+ oldIndex,
167
+ newIndex
168
+ });
169
+ }
170
+ }, items.map((item, index)=>{
171
+ return _react.default.createElement(SortablePill, {
172
+ label: item,
173
+ index: index,
174
+ key: item + index,
175
+ disabled: isDisabled,
176
+ isSortablePillDisabled: isDisabled,
177
+ onRemove: ()=>{
178
+ removeItem(index);
179
+ }
180
+ });
181
+ })), constraints && constraintsType && _react.default.createElement(_TagsEditorConstraints.TagsEditorConstraints, {
182
+ constraints: constraints,
183
+ constraintsType: constraintsType
184
+ }));
185
+ }
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ Object.defineProperty(exports, "TagsEditorConstraints", {
6
+ enumerable: true,
7
+ get: function() {
8
+ return TagsEditorConstraints;
9
+ }
10
+ });
11
+ const _react = _interop_require_wildcard(require("react"));
12
+ const _f36components = require("@contentful/f36-components");
13
+ const _emotion = require("emotion");
14
+ function _getRequireWildcardCache(nodeInterop) {
15
+ if (typeof WeakMap !== "function") return null;
16
+ var cacheBabelInterop = new WeakMap();
17
+ var cacheNodeInterop = new WeakMap();
18
+ return (_getRequireWildcardCache = function(nodeInterop) {
19
+ return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
20
+ })(nodeInterop);
21
+ }
22
+ function _interop_require_wildcard(obj, nodeInterop) {
23
+ if (!nodeInterop && obj && obj.__esModule) {
24
+ return obj;
25
+ }
26
+ if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
27
+ return {
28
+ default: obj
29
+ };
30
+ }
31
+ var cache = _getRequireWildcardCache(nodeInterop);
32
+ if (cache && cache.has(obj)) {
33
+ return cache.get(obj);
34
+ }
35
+ var newObj = {};
36
+ var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
37
+ for(var key in obj){
38
+ if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
39
+ var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
40
+ if (desc && (desc.get || desc.set)) {
41
+ Object.defineProperty(newObj, key, desc);
42
+ } else {
43
+ newObj[key] = obj[key];
44
+ }
45
+ }
46
+ }
47
+ newObj.default = obj;
48
+ if (cache) {
49
+ cache.set(obj, newObj);
50
+ }
51
+ return newObj;
52
+ }
53
+ function TagsEditorConstraints(props) {
54
+ const { constraintsType , constraints } = props;
55
+ return _react.createElement(_f36components.Text, {
56
+ as: "p",
57
+ fontColor: "gray600",
58
+ marginBottom: "none",
59
+ marginTop: "spacingS",
60
+ className: (0, _emotion.css)({
61
+ fontStyle: 'italic'
62
+ }),
63
+ testId: "tag-editor-constraints"
64
+ }, constraintsType === 'min' && _react.createElement("span", null, "Requires at least ", constraints.min, " ", constraints.min === 1 ? 'tag' : 'tags'), constraintsType === 'max' && _react.createElement("span", null, "Requires no more than ", constraints.max, " ", constraints.max === 1 ? 'tag' : 'tags'), constraintsType === 'min-max' && constraints.max !== constraints.min && _react.createElement("span", null, "Requires between ", constraints.min, " and ", constraints.max, " tags"), constraintsType === 'min-max' && constraints.max === constraints.min && _react.createElement("span", null, "Requires exactly ", constraints.max, " ", constraints.max === 1 ? 'tag' : 'tags'));
65
+ }
@@ -0,0 +1,103 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ Object.defineProperty(exports, "TagsEditorContainer", {
6
+ enumerable: true,
7
+ get: function() {
8
+ return TagsEditorContainer;
9
+ }
10
+ });
11
+ const _react = _interop_require_wildcard(require("react"));
12
+ const _fieldeditorshared = require("@contentful/field-editor-shared");
13
+ const _isNumber = _interop_require_default(require("lodash/isNumber"));
14
+ const _TagsEditor = require("./TagsEditor");
15
+ function _interop_require_default(obj) {
16
+ return obj && obj.__esModule ? obj : {
17
+ default: obj
18
+ };
19
+ }
20
+ function _getRequireWildcardCache(nodeInterop) {
21
+ if (typeof WeakMap !== "function") return null;
22
+ var cacheBabelInterop = new WeakMap();
23
+ var cacheNodeInterop = new WeakMap();
24
+ return (_getRequireWildcardCache = function(nodeInterop) {
25
+ return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
26
+ })(nodeInterop);
27
+ }
28
+ function _interop_require_wildcard(obj, nodeInterop) {
29
+ if (!nodeInterop && obj && obj.__esModule) {
30
+ return obj;
31
+ }
32
+ if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
33
+ return {
34
+ default: obj
35
+ };
36
+ }
37
+ var cache = _getRequireWildcardCache(nodeInterop);
38
+ if (cache && cache.has(obj)) {
39
+ return cache.get(obj);
40
+ }
41
+ var newObj = {};
42
+ var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
43
+ for(var key in obj){
44
+ if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
45
+ var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
46
+ if (desc && (desc.get || desc.set)) {
47
+ Object.defineProperty(newObj, key, desc);
48
+ } else {
49
+ newObj[key] = obj[key];
50
+ }
51
+ }
52
+ }
53
+ newObj.default = obj;
54
+ if (cache) {
55
+ cache.set(obj, newObj);
56
+ }
57
+ return newObj;
58
+ }
59
+ function isEmptyTagsValue(value) {
60
+ return value === null || value.length === 0;
61
+ }
62
+ function getConstraintsType(sizeConstraints) {
63
+ if (!sizeConstraints) {
64
+ return undefined;
65
+ }
66
+ if ((0, _isNumber.default)(sizeConstraints.min) && (0, _isNumber.default)(sizeConstraints.max)) {
67
+ return 'min-max';
68
+ } else if ((0, _isNumber.default)(sizeConstraints.min)) {
69
+ return 'min';
70
+ } else if ((0, _isNumber.default)(sizeConstraints.max)) {
71
+ return 'max';
72
+ } else {
73
+ return undefined;
74
+ }
75
+ }
76
+ function TagsEditorContainer(props) {
77
+ const field = props.field;
78
+ const validations = field.validations || [];
79
+ const sizeValidations = validations.filter((validation)=>validation.size).map((validation)=>validation.size);
80
+ const constraints = sizeValidations.length > 0 ? sizeValidations[0] : {};
81
+ const constraintsType = getConstraintsType(constraints);
82
+ return _react.createElement(_fieldeditorshared.FieldConnector, {
83
+ field: field,
84
+ isInitiallyDisabled: props.isInitiallyDisabled,
85
+ isEmptyValue: isEmptyTagsValue,
86
+ throttle: 0
87
+ }, ({ disabled , value , errors , setValue })=>{
88
+ const items = value || [];
89
+ return _react.createElement(_TagsEditor.TagsEditor, {
90
+ constraints: constraints,
91
+ constraintsType: constraintsType,
92
+ isDisabled: disabled,
93
+ hasError: errors.length > 0,
94
+ items: items,
95
+ onUpdate: (items)=>{
96
+ setValue(items);
97
+ }
98
+ });
99
+ });
100
+ }
101
+ TagsEditorContainer.defaultProps = {
102
+ isInitiallyDisabled: true
103
+ };
@@ -0,0 +1,226 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ const _react = _interop_require_wildcard(require("react"));
6
+ const _fieldeditortestutils = require("@contentful/field-editor-test-utils");
7
+ require("@testing-library/jest-dom/extend-expect");
8
+ const _react1 = require("@testing-library/react");
9
+ const _TagsEditorContainer = require("./TagsEditorContainer");
10
+ function _getRequireWildcardCache(nodeInterop) {
11
+ if (typeof WeakMap !== "function") return null;
12
+ var cacheBabelInterop = new WeakMap();
13
+ var cacheNodeInterop = new WeakMap();
14
+ return (_getRequireWildcardCache = function(nodeInterop) {
15
+ return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
16
+ })(nodeInterop);
17
+ }
18
+ function _interop_require_wildcard(obj, nodeInterop) {
19
+ if (!nodeInterop && obj && obj.__esModule) {
20
+ return obj;
21
+ }
22
+ if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
23
+ return {
24
+ default: obj
25
+ };
26
+ }
27
+ var cache = _getRequireWildcardCache(nodeInterop);
28
+ if (cache && cache.has(obj)) {
29
+ return cache.get(obj);
30
+ }
31
+ var newObj = {};
32
+ var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
33
+ for(var key in obj){
34
+ if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
35
+ var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
36
+ if (desc && (desc.get || desc.set)) {
37
+ Object.defineProperty(newObj, key, desc);
38
+ } else {
39
+ newObj[key] = obj[key];
40
+ }
41
+ }
42
+ }
43
+ newObj.default = obj;
44
+ if (cache) {
45
+ cache.set(obj, newObj);
46
+ }
47
+ return newObj;
48
+ }
49
+ (0, _react1.configure)({
50
+ testIdAttribute: 'data-test-id'
51
+ });
52
+ describe('TagsEditor', ()=>{
53
+ afterEach(_react1.cleanup);
54
+ function expectNoConstraints({ queryByTestId }) {
55
+ const $constraints = queryByTestId('tag-editor-constraints');
56
+ expect($constraints).not.toBeInTheDocument();
57
+ }
58
+ function expectInputValue({ getByTestId }, expected) {
59
+ const $input = getByTestId('tag-editor-input');
60
+ expect($input).toHaveValue(expected);
61
+ }
62
+ function expectTagsCount({ queryAllByTestId }, expectedCount) {
63
+ const $values = queryAllByTestId('tag-editor-pill');
64
+ expect($values).toHaveLength(expectedCount);
65
+ }
66
+ function expectTag({ queryAllByTestId }, index, content) {
67
+ const $values = queryAllByTestId('tag-editor-pill');
68
+ expect($values[index].textContent).toEqual(content);
69
+ }
70
+ function typePendingValueAndHitEnter({ getByTestId }, value) {
71
+ const $input = getByTestId('tag-editor-input');
72
+ _react1.fireEvent.change($input, {
73
+ target: {
74
+ value
75
+ }
76
+ });
77
+ _react1.fireEvent.keyDown($input, {
78
+ keyCode: 13
79
+ });
80
+ }
81
+ function clickRemoveTag({ getAllByTestId }, index) {
82
+ _react1.fireEvent.click(getAllByTestId('tag-editor-pill')[index].querySelector('button'));
83
+ }
84
+ it('renders empty value properly', ()=>{
85
+ const [field] = (0, _fieldeditortestutils.createFakeFieldAPI)((mock)=>{
86
+ return {
87
+ ...mock,
88
+ validations: []
89
+ };
90
+ });
91
+ const renderResult = (0, _react1.render)(_react.createElement(_TagsEditorContainer.TagsEditorContainer, {
92
+ field: field,
93
+ isInitiallyDisabled: false
94
+ }));
95
+ expectNoConstraints(renderResult);
96
+ expectInputValue(renderResult, '');
97
+ expectTagsCount(renderResult, 0);
98
+ });
99
+ it('renders non-empty value properly', ()=>{
100
+ const initialValue = [
101
+ 'test1',
102
+ 'test2',
103
+ 'test3'
104
+ ];
105
+ const [field] = (0, _fieldeditortestutils.createFakeFieldAPI)((mock)=>{
106
+ return {
107
+ ...mock,
108
+ validations: []
109
+ };
110
+ }, initialValue);
111
+ const renderResult = (0, _react1.render)(_react.createElement(_TagsEditorContainer.TagsEditorContainer, {
112
+ field: field,
113
+ isInitiallyDisabled: false
114
+ }));
115
+ expectInputValue(renderResult, '');
116
+ expectNoConstraints(renderResult);
117
+ expectTagsCount(renderResult, 3);
118
+ expectTag(renderResult, 0, 'test1');
119
+ expectTag(renderResult, 1, 'test2');
120
+ expectTag(renderResult, 2, 'test3');
121
+ });
122
+ describe('renders constraints message', ()=>{
123
+ const conditions = [
124
+ {
125
+ testType: 'max',
126
+ validation: {
127
+ max: 10
128
+ },
129
+ expected: 'Requires no more than 10 tags'
130
+ },
131
+ {
132
+ testType: 'max-one',
133
+ validation: {
134
+ max: 1
135
+ },
136
+ expected: 'Requires no more than 1 tag'
137
+ },
138
+ {
139
+ testType: 'min',
140
+ validation: {
141
+ min: 20
142
+ },
143
+ expected: 'Requires at least 20 tags'
144
+ },
145
+ {
146
+ testType: 'min-one',
147
+ validation: {
148
+ min: 1
149
+ },
150
+ expected: 'Requires at least 1 tag'
151
+ },
152
+ {
153
+ testType: 'min-max',
154
+ validation: {
155
+ min: 10,
156
+ max: 20
157
+ },
158
+ expected: 'Requires between 10 and 20 tags'
159
+ },
160
+ {
161
+ testType: 'min-max-equal',
162
+ validation: {
163
+ min: 10,
164
+ max: 10
165
+ },
166
+ expected: 'Requires exactly 10 tags'
167
+ }
168
+ ];
169
+ conditions.forEach((condition)=>{
170
+ it(condition.testType, ()=>{
171
+ const [field] = (0, _fieldeditortestutils.createFakeFieldAPI)((mock)=>{
172
+ return {
173
+ ...mock,
174
+ validations: [
175
+ {
176
+ size: condition.validation
177
+ }
178
+ ]
179
+ };
180
+ });
181
+ const { getByTestId } = (0, _react1.render)(_react.createElement(_TagsEditorContainer.TagsEditorContainer, {
182
+ field: field,
183
+ isInitiallyDisabled: false
184
+ }));
185
+ const $constraints = getByTestId('tag-editor-constraints');
186
+ expect($constraints.textContent).toEqual(condition.expected);
187
+ });
188
+ });
189
+ });
190
+ it('adds and removes values', ()=>{
191
+ const [field] = (0, _fieldeditortestutils.createFakeFieldAPI)((field)=>{
192
+ jest.spyOn(field, 'setValue');
193
+ jest.spyOn(field, 'removeValue');
194
+ return {
195
+ ...field,
196
+ validations: []
197
+ };
198
+ });
199
+ const renderResult = (0, _react1.render)(_react.createElement(_TagsEditorContainer.TagsEditorContainer, {
200
+ field: field,
201
+ isInitiallyDisabled: false
202
+ }));
203
+ typePendingValueAndHitEnter(renderResult, 'first item');
204
+ expect(field.setValue).toHaveBeenCalledWith([
205
+ 'first item'
206
+ ]);
207
+ expect(field.setValue).toHaveBeenCalledTimes(1);
208
+ expect(field.removeValue).toHaveBeenCalledTimes(0);
209
+ expectInputValue(renderResult, '');
210
+ typePendingValueAndHitEnter(renderResult, 'second item');
211
+ expect(field.setValue).toHaveBeenCalledWith([
212
+ 'first item',
213
+ 'second item'
214
+ ]);
215
+ expect(field.setValue).toHaveBeenCalledTimes(2);
216
+ expectInputValue(renderResult, '');
217
+ clickRemoveTag(renderResult, 0);
218
+ expect(field.setValue).toHaveBeenCalledWith([
219
+ 'second item'
220
+ ]);
221
+ expect(field.setValue).toHaveBeenCalledTimes(3);
222
+ clickRemoveTag(renderResult, 0);
223
+ expect(field.setValue).toHaveBeenCalledTimes(3);
224
+ expect(field.removeValue).toHaveBeenCalledTimes(1);
225
+ });
226
+ });
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ Object.defineProperty(exports, "TagsEditor", {
6
+ enumerable: true,
7
+ get: function() {
8
+ return TagsEditor;
9
+ }
10
+ });
11
+ const _TagsEditorContainer = require("./TagsEditorContainer");
12
+ const TagsEditor = _TagsEditorContainer.TagsEditorContainer;
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });