@k-int/stripes-kint-components 5.8.2 → 5.8.3

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/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ ## [5.8.3](https://gitlab.com/knowledge-integration/folio/stripes-kint-components/compare/v5.8.2...v5.8.3) (2024-10-23)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * ERM-3391--NumberField doesnt update display value from form state changes ([cb44d06](https://gitlab.com/knowledge-integration/folio/stripes-kint-components/commit/cb44d063aed221cb544204b8b5d9df6dc313e9c2))
7
+
1
8
  ## [5.8.2](https://gitlab.com/knowledge-integration/folio/stripes-kint-components/compare/v5.8.1...v5.8.2) (2024-09-11)
2
9
 
3
10
 
@@ -31,13 +31,18 @@ const NumberField = props => {
31
31
 
32
32
  // Allow direct control of field
33
33
  (0, _react.useEffect)(() => {
34
- if (value && numValue !== value) {
35
- setNumValue(value);
34
+ const valueToUse = value ?? input.value;
35
+ if (!valueToUse && numValue) {
36
+ // Make sure to empty out if it's cleared. Treating '' as empty instead of undefined
37
+ setNumValue('');
38
+ }
39
+ if (valueToUse && numValue !== valueToUse) {
40
+ setNumValue(valueToUse);
36
41
  }
37
- if (numValue && forceControl !== numValue) {
42
+ if (forceControl !== numValue) {
38
43
  setForceControl(numValue);
39
44
  }
40
- }, [forceControl, numValue, value]);
45
+ }, [forceControl, numValue, value, input]);
41
46
  const handleChange = e => {
42
47
  // Actually set the value in the form
43
48
  if (input?.onChange) {
@@ -55,7 +60,7 @@ const NumberField = props => {
55
60
  const handleUserChange = e => {
56
61
  const parsedValue = parseFloat(e.target.value);
57
62
 
58
- // ReturnValue needed for contorlled components
63
+ // ReturnValue needed for controlled components
59
64
  if (parsedValue || parsedValue === 0) {
60
65
  setNumValue(parsedValue);
61
66
  changeField(parsedValue);
@@ -83,6 +88,7 @@ const NumberField = props => {
83
88
  ref: inputRef,
84
89
  ...input,
85
90
  hidden: true,
91
+ id: input?.name,
86
92
  onChange: handleChange,
87
93
  type: "number",
88
94
  value: numValue
@@ -93,6 +99,7 @@ NumberField.propTypes = {
93
99
  onBlur: _propTypes.default.func,
94
100
  onChange: _propTypes.default.func,
95
101
  input: _propTypes.default.shape({
102
+ name: _propTypes.default.string.isRequired,
96
103
  onChange: _propTypes.default.func.isRequired,
97
104
  onBlur: _propTypes.default.func,
98
105
  value: _propTypes.default.oneOfType([_propTypes.default.number, _propTypes.default.string])
@@ -0,0 +1,200 @@
1
+ "use strict";
2
+
3
+ var _react = require("react");
4
+ var _reactFinalForm = require("react-final-form");
5
+ var _userEvent = _interopRequireDefault(require("@folio/jest-config-stripes/testing-library/user-event"));
6
+ var _react2 = require("@folio/jest-config-stripes/testing-library/react");
7
+ var _stripesErmTesting = require("@folio/stripes-erm-testing");
8
+ var _components = require("@folio/stripes/components");
9
+ var _NumberField = _interopRequireDefault(require("./NumberField"));
10
+ var _helpers = require("../../../test/jest/helpers");
11
+ var _jsxRuntime = require("react/jsx-runtime");
12
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
13
+ const onSubmit = jest.fn();
14
+ const NUMBER_FIELD_LABEL = 'TEST NUMBER FIELD';
15
+ const NUMBER_FIELD_ID = 'number-field-test';
16
+ const NUMBER_FIELD_INPUT_ID = 'input-id-test';
17
+ const RESET_BUTTON_LABEL = 'RESET NUMBER FIELD';
18
+
19
+ // We need to use jest selectors instead of interactors as the TextField interactor
20
+ // can't handle the double-text-field nature of NumberField
21
+ const getTextField = () => {
22
+ // Spinbutton because it's a "number" type textField...
23
+ return _react2.screen.getByRole('spinbutton', {
24
+ name: NUMBER_FIELD_LABEL
25
+ });
26
+ };
27
+ const submitForm = async () => {
28
+ await (0, _react2.waitFor)(async () => {
29
+ await (0, _stripesErmTesting.Button)('Submit').click();
30
+ });
31
+ };
32
+ const SpecialResetButton = () => {
33
+ const {
34
+ change
35
+ } = (0, _reactFinalForm.useForm)();
36
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Button, {
37
+ onClick: () => change(NUMBER_FIELD_INPUT_ID, undefined),
38
+ children: RESET_BUTTON_LABEL
39
+ });
40
+ };
41
+ const NonControlledComponent = () => {
42
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
43
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(SpecialResetButton, {}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactFinalForm.Field, {
44
+ component: _NumberField.default,
45
+ id: NUMBER_FIELD_ID,
46
+ label: NUMBER_FIELD_LABEL,
47
+ name: NUMBER_FIELD_INPUT_ID
48
+ })]
49
+ });
50
+ };
51
+ const ControlledComponent = () => {
52
+ const [value, setValue] = (0, _react.useState)();
53
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
54
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(SpecialResetButton, {}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactFinalForm.Field, {
55
+ component: _NumberField.default,
56
+ id: NUMBER_FIELD_ID,
57
+ label: NUMBER_FIELD_LABEL,
58
+ name: NUMBER_FIELD_INPUT_ID,
59
+ onChange: e => setValue(e.target.value),
60
+ value: value
61
+ })]
62
+ });
63
+ };
64
+
65
+ // EXAMPLE nesting repeated tests with describe.each or test.each might speed up some of our test writing
66
+ describe.each([['Non-Controlled', /*#__PURE__*/(0, _jsxRuntime.jsx)(NonControlledComponent, {})], ['Controlled', /*#__PURE__*/(0, _jsxRuntime.jsx)(ControlledComponent, {})]])('NumberField', (controlType, component) => {
67
+ let _renderComponent;
68
+ let textField;
69
+ describe(`Regular usage (${controlType})`, () => {
70
+ beforeEach(async () => {
71
+ onSubmit.mockClear();
72
+ _renderComponent = (0, _helpers.renderWithKintHarness)(/*#__PURE__*/(0, _jsxRuntime.jsx)(_stripesErmTesting.TestForm, {
73
+ initialValues: {},
74
+ onSubmit: onSubmit,
75
+ children: component
76
+ }));
77
+ textField = getTextField();
78
+ });
79
+ it('renders text field as expected', () => {
80
+ expect(textField).toBeInTheDocument();
81
+ });
82
+ describe.each([['non-numeric characters', 'sdhukjasklfs', '', {}], ['numeric characters', '12345', '12345', {
83
+ [NUMBER_FIELD_INPUT_ID]: '12345'
84
+ }], ['scientific notation', '1e5', '100000', {
85
+ [NUMBER_FIELD_INPUT_ID]: '100000'
86
+ }], ['negative numbers', '-100', '-100', {
87
+ [NUMBER_FIELD_INPUT_ID]: '-100'
88
+ }]])('Typing', (characterType, typedChars, expectedDisplay, expectedSubmit) => {
89
+ describe(`typing ${characterType}`, () => {
90
+ beforeEach(async () => {
91
+ await (0, _react2.waitFor)(async () => {
92
+ await _userEvent.default.type(textField, typedChars);
93
+ });
94
+ });
95
+ it('does not render typed characters', async () => {
96
+ expect(textField).toHaveDisplayValue(expectedDisplay);
97
+ });
98
+ describe('submitting the form', () => {
99
+ beforeEach(submitForm);
100
+ it('submits with expected values', () => {
101
+ expect(onSubmit.mock.calls[0][0]).toEqual(expectedSubmit);
102
+ });
103
+ });
104
+ });
105
+ });
106
+ });
107
+ describe.each([['numeric initialvalues', {
108
+ [NUMBER_FIELD_INPUT_ID]: '7654321'
109
+ }, '7654321', {
110
+ [NUMBER_FIELD_INPUT_ID]: '7654321'
111
+ }], ['regular scientific initialvalue NOTE: scientific value parsing does NOT work from initialValues', {
112
+ [NUMBER_FIELD_INPUT_ID]: '3e7'
113
+ }, '3e7', {
114
+ [NUMBER_FIELD_INPUT_ID]: '3e7'
115
+ }], ['negative initialvalue', {
116
+ [NUMBER_FIELD_INPUT_ID]: '-100'
117
+ }, '-100', {
118
+ [NUMBER_FIELD_INPUT_ID]: '-100'
119
+ }]])(`Initial values (${controlType})`, (describeTitle, initialValues, displayValue, submitValue) => {
120
+ describe(describeTitle, () => {
121
+ beforeEach(async () => {
122
+ onSubmit.mockClear();
123
+ _renderComponent = (0, _helpers.renderWithKintHarness)(/*#__PURE__*/(0, _jsxRuntime.jsx)(_stripesErmTesting.TestForm, {
124
+ initialValues: initialValues,
125
+ onSubmit: onSubmit,
126
+ children: component
127
+ }));
128
+ textField = getTextField();
129
+ });
130
+ it('renders initial value', async () => {
131
+ expect(textField).toHaveDisplayValue(displayValue);
132
+ });
133
+ describe('submitting the form', () => {
134
+ beforeEach(submitForm);
135
+ it('submits with expected values', () => {
136
+ expect(onSubmit.mock.calls[0][0]).toEqual(submitValue);
137
+ });
138
+ });
139
+ });
140
+ });
141
+ describe.each([['Clear after typing', async () => {
142
+ onSubmit.mockClear();
143
+ _renderComponent = (0, _helpers.renderWithKintHarness)(/*#__PURE__*/(0, _jsxRuntime.jsx)(_stripesErmTesting.TestForm, {
144
+ initialValues: {},
145
+ onSubmit: onSubmit,
146
+ children: component
147
+ }));
148
+ textField = getTextField();
149
+ await (0, _react2.waitFor)(async () => {
150
+ await _userEvent.default.type(textField, '32786843');
151
+ });
152
+ }], ['Clear from initialValues', async () => {
153
+ onSubmit.mockClear();
154
+ _renderComponent = (0, _helpers.renderWithKintHarness)(/*#__PURE__*/(0, _jsxRuntime.jsx)(_stripesErmTesting.TestForm, {
155
+ initialValues: {
156
+ [NUMBER_FIELD_INPUT_ID]: '32786843'
157
+ },
158
+ onSubmit: onSubmit,
159
+ children: component
160
+ }));
161
+ textField = getTextField();
162
+ }]])(`ERM-3391: state change underneath component (${controlType})`, (clearType, beforeEachFunc) => {
163
+ describe(clearType, () => {
164
+ beforeEach(beforeEachFunc);
165
+ it('renders text field', () => {
166
+ expect(textField).toBeInTheDocument();
167
+ });
168
+ it('renders special reset button', async () => {
169
+ await (0, _stripesErmTesting.Button)(RESET_BUTTON_LABEL).exists();
170
+ });
171
+ describe.each([['Control', false, '32786843', {
172
+ [NUMBER_FIELD_INPUT_ID]: '32786843'
173
+ }], ['After reset', true, '', {}]])('Test reset', (describeTitle, clearField, expectedValue, expectedSubmit) => {
174
+ describe(describeTitle, () => {
175
+ beforeEach(async () => {
176
+ onSubmit.mockClear();
177
+ if (clearField) {
178
+ await (0, _react2.waitFor)(async () => {
179
+ await (0, _stripesErmTesting.Button)(RESET_BUTTON_LABEL).click();
180
+ });
181
+ }
182
+ });
183
+ it(`renders text field with display value: ${expectedValue}`, async () => {
184
+ await (0, _react2.waitFor)(() => {
185
+ expect(textField).toHaveDisplayValue(expectedValue);
186
+ });
187
+ });
188
+ describe('submitting', () => {
189
+ describe('submitting the form', () => {
190
+ beforeEach(submitForm);
191
+ it('submits with expected values', () => {
192
+ expect(onSubmit.mock.calls[0][0]).toEqual(expectedSubmit);
193
+ });
194
+ });
195
+ });
196
+ });
197
+ });
198
+ });
199
+ });
200
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@k-int/stripes-kint-components",
3
- "version": "5.8.2",
3
+ "version": "5.8.3",
4
4
  "description": "Stripes Component library for K-Int specific applications",
5
5
  "sideEffects": [
6
6
  "*.css"
@@ -37,14 +37,21 @@ const NumberField = (props) => {
37
37
 
38
38
  // Allow direct control of field
39
39
  useEffect(() => {
40
- if (value && numValue !== value) {
41
- setNumValue(value);
40
+ const valueToUse = value ?? input.value;
41
+
42
+ if (!valueToUse && numValue) {
43
+ // Make sure to empty out if it's cleared. Treating '' as empty instead of undefined
44
+ setNumValue('');
45
+ }
46
+
47
+ if (valueToUse && numValue !== valueToUse) {
48
+ setNumValue(valueToUse);
42
49
  }
43
50
 
44
- if (numValue && forceControl !== numValue) {
51
+ if (forceControl !== numValue) {
45
52
  setForceControl(numValue);
46
53
  }
47
- }, [forceControl, numValue, value]);
54
+ }, [forceControl, numValue, value, input]);
48
55
 
49
56
  const handleChange = (e) => {
50
57
  // Actually set the value in the form
@@ -61,12 +68,10 @@ const NumberField = (props) => {
61
68
  }
62
69
  };
63
70
 
64
-
65
-
66
71
  const handleUserChange = (e) => {
67
72
  const parsedValue = parseFloat(e.target.value);
68
73
 
69
- // ReturnValue needed for contorlled components
74
+ // ReturnValue needed for controlled components
70
75
  if (parsedValue || parsedValue === 0) {
71
76
  setNumValue(parsedValue);
72
77
  changeField(parsedValue);
@@ -96,6 +101,7 @@ const NumberField = (props) => {
96
101
  ref={inputRef}
97
102
  {...input}
98
103
  hidden
104
+ id={input?.name}
99
105
  onChange={handleChange}
100
106
  type="number"
101
107
  value={numValue}
@@ -108,6 +114,7 @@ NumberField.propTypes = {
108
114
  onBlur: PropTypes.func,
109
115
  onChange: PropTypes.func,
110
116
  input: PropTypes.shape({
117
+ name: PropTypes.string.isRequired,
111
118
  onChange: PropTypes.func.isRequired,
112
119
  onBlur: PropTypes.func,
113
120
  value: PropTypes.oneOfType([
@@ -0,0 +1,258 @@
1
+ import { useState } from 'react';
2
+ import { Field, useForm } from 'react-final-form';
3
+
4
+ import userEvent from '@folio/jest-config-stripes/testing-library/user-event';
5
+ import { screen, waitFor } from '@folio/jest-config-stripes/testing-library/react';
6
+
7
+ import { Button, TestForm, TextField } from '@folio/stripes-erm-testing';
8
+ import { Button as StripesButton } from '@folio/stripes/components';
9
+
10
+ import NumberField from './NumberField';
11
+
12
+ import { renderWithKintHarness } from '../../../test/jest/helpers';
13
+
14
+
15
+ const onSubmit = jest.fn();
16
+
17
+ const NUMBER_FIELD_LABEL = 'TEST NUMBER FIELD';
18
+ const NUMBER_FIELD_ID = 'number-field-test';
19
+ const NUMBER_FIELD_INPUT_ID = 'input-id-test';
20
+
21
+ const RESET_BUTTON_LABEL = 'RESET NUMBER FIELD';
22
+
23
+ // We need to use jest selectors instead of interactors as the TextField interactor
24
+ // can't handle the double-text-field nature of NumberField
25
+ const getTextField = () => {
26
+ // Spinbutton because it's a "number" type textField...
27
+ return screen.getByRole('spinbutton', { name: NUMBER_FIELD_LABEL });
28
+ };
29
+
30
+ const submitForm = async () => {
31
+ await waitFor(async () => {
32
+ await Button('Submit').click();
33
+ });
34
+ };
35
+
36
+ const SpecialResetButton = () => {
37
+ const { change } = useForm();
38
+
39
+ return (
40
+ <StripesButton
41
+ onClick={() => change(NUMBER_FIELD_INPUT_ID, undefined)}
42
+ >
43
+ {RESET_BUTTON_LABEL}
44
+ </StripesButton>
45
+ );
46
+ };
47
+
48
+ const NonControlledComponent = () => {
49
+ return (
50
+ <>
51
+ <SpecialResetButton />
52
+ <Field
53
+ component={NumberField}
54
+ id={NUMBER_FIELD_ID}
55
+ label={NUMBER_FIELD_LABEL}
56
+ name={NUMBER_FIELD_INPUT_ID}
57
+ />
58
+ </>
59
+ );
60
+ };
61
+
62
+ const ControlledComponent = () => {
63
+ const [value, setValue] = useState();
64
+
65
+ return (
66
+ <>
67
+ <SpecialResetButton />
68
+ <Field
69
+ component={NumberField}
70
+ id={NUMBER_FIELD_ID}
71
+ label={NUMBER_FIELD_LABEL}
72
+ name={NUMBER_FIELD_INPUT_ID}
73
+ onChange={e => setValue(e.target.value)}
74
+ value={value}
75
+ />
76
+ </>
77
+ );
78
+ };
79
+
80
+ // EXAMPLE nesting repeated tests with describe.each or test.each might speed up some of our test writing
81
+ describe.each([
82
+ ['Non-Controlled', <NonControlledComponent />],
83
+ ['Controlled', <ControlledComponent />],
84
+ ])('NumberField', (controlType, component) => {
85
+ let _renderComponent;
86
+ let textField;
87
+ describe(`Regular usage (${controlType})`, () => {
88
+ beforeEach(async () => {
89
+ onSubmit.mockClear();
90
+ _renderComponent = renderWithKintHarness(
91
+ <TestForm
92
+ initialValues={{}}
93
+ onSubmit={onSubmit}
94
+ >
95
+ {component}
96
+ </TestForm>
97
+ );
98
+
99
+ textField = getTextField();
100
+ });
101
+
102
+ it('renders text field as expected', () => {
103
+ expect(textField).toBeInTheDocument();
104
+ });
105
+
106
+ describe.each([
107
+ ['non-numeric characters', 'sdhukjasklfs', '', {}],
108
+ ['numeric characters', '12345', '12345', { [NUMBER_FIELD_INPUT_ID]: '12345' }],
109
+ ['scientific notation', '1e5', '100000', { [NUMBER_FIELD_INPUT_ID]: '100000' }],
110
+ ['negative numbers', '-100', '-100', { [NUMBER_FIELD_INPUT_ID]: '-100' }],
111
+ ])('Typing', (characterType, typedChars, expectedDisplay, expectedSubmit) => {
112
+ describe(`typing ${characterType}`, () => {
113
+ beforeEach(async () => {
114
+ await waitFor(async () => {
115
+ await userEvent.type(textField, typedChars);
116
+ });
117
+ });
118
+
119
+ it('does not render typed characters', async () => {
120
+ expect(textField).toHaveDisplayValue(expectedDisplay);
121
+ });
122
+
123
+ describe('submitting the form', () => {
124
+ beforeEach(submitForm);
125
+
126
+ it('submits with expected values', () => {
127
+ expect(onSubmit.mock.calls[0][0]).toEqual(expectedSubmit);
128
+ });
129
+ });
130
+ });
131
+ });
132
+ });
133
+
134
+ describe.each([
135
+ ['numeric initialvalues', { [NUMBER_FIELD_INPUT_ID]: '7654321' }, '7654321', { [NUMBER_FIELD_INPUT_ID]: '7654321' }],
136
+ ['regular scientific initialvalue NOTE: scientific value parsing does NOT work from initialValues', { [NUMBER_FIELD_INPUT_ID]: '3e7' }, '3e7', { [NUMBER_FIELD_INPUT_ID]: '3e7' }],
137
+ ['negative initialvalue', { [NUMBER_FIELD_INPUT_ID]: '-100' }, '-100', { [NUMBER_FIELD_INPUT_ID]: '-100' }],
138
+ ])(`Initial values (${controlType})`, (describeTitle, initialValues, displayValue, submitValue) => {
139
+ describe(describeTitle, () => {
140
+ beforeEach(async () => {
141
+ onSubmit.mockClear();
142
+ _renderComponent = renderWithKintHarness(
143
+ <TestForm
144
+ initialValues={initialValues}
145
+ onSubmit={onSubmit}
146
+ >
147
+ {component}
148
+ </TestForm>
149
+ );
150
+
151
+ textField = getTextField();
152
+ });
153
+
154
+ it('renders initial value', async () => {
155
+ expect(textField).toHaveDisplayValue(displayValue);
156
+ });
157
+
158
+ describe('submitting the form', () => {
159
+ beforeEach(submitForm);
160
+
161
+ it('submits with expected values', () => {
162
+ expect(onSubmit.mock.calls[0][0]).toEqual(submitValue);
163
+ });
164
+ });
165
+ });
166
+ });
167
+
168
+ describe.each([
169
+ [
170
+ 'Clear after typing',
171
+ async () => {
172
+ onSubmit.mockClear();
173
+ _renderComponent = renderWithKintHarness(
174
+ <TestForm
175
+ initialValues={{}}
176
+ onSubmit={onSubmit}
177
+ >
178
+ {component}
179
+ </TestForm>
180
+ );
181
+ textField = getTextField();
182
+
183
+ await waitFor(async () => {
184
+ await userEvent.type(textField, '32786843');
185
+ });
186
+ }
187
+ ],
188
+ [
189
+ 'Clear from initialValues',
190
+ async () => {
191
+ onSubmit.mockClear();
192
+ _renderComponent = renderWithKintHarness(
193
+ <TestForm
194
+ initialValues={{ [NUMBER_FIELD_INPUT_ID]: '32786843' }}
195
+ onSubmit={onSubmit}
196
+ >
197
+ {component}
198
+ </TestForm>
199
+ );
200
+ textField = getTextField();
201
+ }
202
+ ]
203
+ ])(`ERM-3391: state change underneath component (${controlType})`, (clearType, beforeEachFunc) => {
204
+ describe(clearType, () => {
205
+ beforeEach(beforeEachFunc);
206
+
207
+ it('renders text field', () => {
208
+ expect(textField).toBeInTheDocument();
209
+ });
210
+
211
+ it('renders special reset button', async () => {
212
+ await Button(RESET_BUTTON_LABEL).exists();
213
+ });
214
+
215
+ describe.each([
216
+ [
217
+ 'Control',
218
+ false,
219
+ '32786843',
220
+ { [NUMBER_FIELD_INPUT_ID]: '32786843' }
221
+ ],
222
+ [
223
+ 'After reset',
224
+ true,
225
+ '',
226
+ {}
227
+ ]
228
+ ])('Test reset', (describeTitle, clearField, expectedValue, expectedSubmit) => {
229
+ describe(describeTitle, () => {
230
+ beforeEach(async () => {
231
+ onSubmit.mockClear();
232
+ if (clearField) {
233
+ await waitFor(async () => {
234
+ await Button(RESET_BUTTON_LABEL).click();
235
+ });
236
+ }
237
+ });
238
+
239
+ it(`renders text field with display value: ${expectedValue}`, async () => {
240
+ await waitFor(() => {
241
+ expect(textField).toHaveDisplayValue(expectedValue);
242
+ });
243
+ });
244
+
245
+ describe('submitting', () => {
246
+ describe('submitting the form', () => {
247
+ beforeEach(submitForm);
248
+
249
+ it('submits with expected values', () => {
250
+ expect(onSubmit.mock.calls[0][0]).toEqual(expectedSubmit);
251
+ });
252
+ });
253
+ });
254
+ });
255
+ });
256
+ });
257
+ });
258
+ });