@financial-times/n-conversion-forms 41.0.4 → 41.1.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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "branch": "",
3
3
  "repo": "n-conversion-forms",
4
- "version": "028ebcce5ada092046618b69deb793c2d6407133",
5
- "tag": "v41.0.4",
6
- "buildNumber": "15627"
4
+ "version": "934904541de6ceb44cf85d8171392787478cd084",
5
+ "tag": "v41.1.0",
6
+ "buildNumber": "15757"
7
7
  }
package/README.md CHANGED
@@ -345,7 +345,7 @@ You can add some custom validation functionality as follows:
345
345
 
346
346
  ```js
347
347
  validation.addCustomValidation({
348
- errorMessage: 'Custom error message here.',
348
+ errorMessage: 'Custom error message here.', // can be a string or a function return that returns a string
349
349
  field: domElementToValidate,
350
350
  validator: () => {
351
351
  // Custom validation code here. *Must* return truthy or falsey.
@@ -355,6 +355,20 @@ validation.addCustomValidation({
355
355
 
356
356
  If the validation fails, the field will look like it usually does when validation fails with your specified message displayed underneath.
357
357
 
358
+ ### YearOfBirth
359
+
360
+ The Year of Birth utility provides a more friendly way to get the value in the input field and add event listener on blur.
361
+
362
+ ```js
363
+ const yearOfBirth = new YearOfBirth(document));
364
+ // Get the value in the input field
365
+ const yearOfBirthInputValue = yearOfBirth.getYearOfBirth();
366
+ // add event listener to the input field on blur
367
+ yearOfBirth.handleYearOfBirthBlur(() => {
368
+ doSomething();
369
+ });
370
+ ```
371
+
358
372
  ### Zuora
359
373
 
360
374
  The Zuora utility aims to wrap the Zuora client side library to make it more user friendly.
@@ -0,0 +1,200 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`YearOfBirth render a year of birth input with default value 1`] = `
4
+ <label id="yearOfBirthField"
5
+ for="yearOfBirth"
6
+ class="o-forms-field ncf__validation-error"
7
+ data-validate="required,number"
8
+ >
9
+ <span class="o-forms-title">
10
+ <span class="o-forms-title__main">
11
+ Year of birth
12
+ </span>
13
+ <span class="o-forms-title__prompt">
14
+ </span>
15
+ </span>
16
+ <span class="o-forms-input o-forms-input--text">
17
+ <input type="number"
18
+ id="yearOfBirth"
19
+ name="yearOfBirth"
20
+ placeholder="YYYY"
21
+ autocomplete="bday-year"
22
+ minlength="4"
23
+ maxlength="4"
24
+ data-trackable="year-of-birth"
25
+ aria-required="true"
26
+ required
27
+ value="2001"
28
+ >
29
+ <span class="o-forms-input__error">
30
+ Please enter a valid year of birth
31
+ </span>
32
+ </span>
33
+ </label>
34
+ `;
35
+
36
+ exports[`YearOfBirth render a year of birth input with disabled field 1`] = `
37
+ <label id="yearOfBirthField"
38
+ for="yearOfBirth"
39
+ class="o-forms-field ncf__validation-error"
40
+ data-validate="required,number"
41
+ >
42
+ <span class="o-forms-title">
43
+ <span class="o-forms-title__main">
44
+ Year of birth
45
+ </span>
46
+ <span class="o-forms-title__prompt">
47
+ </span>
48
+ </span>
49
+ <span class="o-forms-input o-forms-input--text">
50
+ <input type="number"
51
+ id="yearOfBirth"
52
+ name="yearOfBirth"
53
+ placeholder="YYYY"
54
+ autocomplete="bday-year"
55
+ minlength="4"
56
+ maxlength="4"
57
+ data-trackable="year-of-birth"
58
+ aria-required="true"
59
+ required
60
+ disabled
61
+ value="2001"
62
+ >
63
+ <span class="o-forms-input__error">
64
+ Please enter a valid year of birth
65
+ </span>
66
+ </span>
67
+ </label>
68
+ `;
69
+
70
+ exports[`YearOfBirth renders a year of birth input with custom prompt 1`] = `
71
+ <label id="yearOfBirthField"
72
+ for="yearOfBirth"
73
+ class="o-forms-field ncf__validation-error"
74
+ data-validate="required,number"
75
+ >
76
+ <span class="o-forms-title">
77
+ <span class="o-forms-title__main">
78
+ Year of birth
79
+ </span>
80
+ <span class="o-forms-title__prompt">
81
+ This helps us ensure people of all ages find value in the FT
82
+ </span>
83
+ </span>
84
+ <span class="o-forms-input o-forms-input--text">
85
+ <input type="number"
86
+ id="yearOfBirth"
87
+ name="yearOfBirth"
88
+ placeholder="YYYY"
89
+ autocomplete="bday-year"
90
+ minlength="4"
91
+ maxlength="4"
92
+ data-trackable="year-of-birth"
93
+ aria-required="true"
94
+ required
95
+ value
96
+ >
97
+ <span class="o-forms-input__error">
98
+ Please enter a valid year of birth
99
+ </span>
100
+ </span>
101
+ </label>
102
+ `;
103
+
104
+ exports[`YearOfBirth renders a year of birth input with default error message 1`] = `
105
+ <label id="yearOfBirthField"
106
+ for="yearOfBirth"
107
+ class="o-forms-field ncf__validation-error"
108
+ data-validate="required,number"
109
+ >
110
+ <span class="o-forms-title">
111
+ <span class="o-forms-title__main">
112
+ Year of birth
113
+ </span>
114
+ <span class="o-forms-title__prompt">
115
+ </span>
116
+ </span>
117
+ <span class="o-forms-input o-forms-input--text o-forms-input--invalid">
118
+ <input type="number"
119
+ id="yearOfBirth"
120
+ name="yearOfBirth"
121
+ placeholder="YYYY"
122
+ autocomplete="bday-year"
123
+ minlength="4"
124
+ maxlength="4"
125
+ data-trackable="year-of-birth"
126
+ aria-required="true"
127
+ required
128
+ value
129
+ >
130
+ <span class="o-forms-input__error">
131
+ Please enter a valid year of birth
132
+ </span>
133
+ </span>
134
+ </label>
135
+ `;
136
+
137
+ exports[`YearOfBirth renders a year of birth input with default params 1`] = `
138
+ <label id="yearOfBirthField"
139
+ for="yearOfBirth"
140
+ class="o-forms-field ncf__validation-error"
141
+ data-validate="required,number"
142
+ >
143
+ <span class="o-forms-title">
144
+ <span class="o-forms-title__main">
145
+ Year of birth
146
+ </span>
147
+ <span class="o-forms-title__prompt">
148
+ </span>
149
+ </span>
150
+ <span class="o-forms-input o-forms-input--text">
151
+ <input type="number"
152
+ id="yearOfBirth"
153
+ name="yearOfBirth"
154
+ placeholder="YYYY"
155
+ autocomplete="bday-year"
156
+ minlength="4"
157
+ maxlength="4"
158
+ data-trackable="year-of-birth"
159
+ aria-required="true"
160
+ required
161
+ value
162
+ >
163
+ <span class="o-forms-input__error">
164
+ Please enter a valid year of birth
165
+ </span>
166
+ </span>
167
+ </label>
168
+ `;
169
+
170
+ exports[`YearOfBirth renders an optional year of birth input 1`] = `
171
+ <label id="yearOfBirthField"
172
+ for="yearOfBirth"
173
+ class="o-forms-field ncf__validation-error"
174
+ data-validate="required,number"
175
+ >
176
+ <span class="o-forms-title o-forms-field--optional">
177
+ <span class="o-forms-title__main">
178
+ Year of birth
179
+ </span>
180
+ <span class="o-forms-title__prompt">
181
+ </span>
182
+ </span>
183
+ <span class="o-forms-input o-forms-input--text">
184
+ <input type="number"
185
+ id="yearOfBirth"
186
+ name="yearOfBirth"
187
+ placeholder="YYYY"
188
+ autocomplete="bday-year"
189
+ minlength="4"
190
+ maxlength="4"
191
+ data-trackable="year-of-birth"
192
+ aria-required="false"
193
+ value
194
+ >
195
+ <span class="o-forms-input__error">
196
+ Please enter a valid year of birth
197
+ </span>
198
+ </span>
199
+ </label>
200
+ `;
@@ -57,3 +57,4 @@ export { GraduationDate } from './graduation-date';
57
57
  export { GoogleSignIn } from './google-sign-in';
58
58
  export { TextInput } from './text-input';
59
59
  export { DeferredBillingTerms } from './deferred-billing-terms';
60
+ export { YearOfBirth } from './year-of-birth';
@@ -1,8 +1,7 @@
1
1
  import { Industry } from './index';
2
2
  import { expectToRenderCorrectly } from '../test-jest/helpers/expect-to-render-correctly';
3
- import { demographics } from 'n-common-static-data';
3
+ import { demographics } from '../helpers/demographics';
4
4
  const defaultOptions = demographics.industries.industries;
5
-
6
5
  import Enzyme, { mount } from 'enzyme';
7
6
  import Adapter from 'enzyme-adapter-react-16';
8
7
  Enzyme.configure({ adapter: new Adapter() });
@@ -1,6 +1,6 @@
1
1
  import { Position } from './index';
2
2
  import { expectToRenderCorrectly } from '../test-jest/helpers/expect-to-render-correctly';
3
- import { demographics } from 'n-common-static-data';
3
+ import { demographics } from '../helpers/demographics';
4
4
  const B2CPositions = demographics.positions.positions;
5
5
 
6
6
  import Enzyme, { mount } from 'enzyme';
@@ -1,6 +1,6 @@
1
1
  import { Responsibility } from './index';
2
2
  import { expectToRenderCorrectly } from '../test-jest/helpers/expect-to-render-correctly';
3
- import { demographics } from 'n-common-static-data';
3
+ import { demographics } from '../helpers/demographics';
4
4
  const B2CResponsibilities = demographics.responsibilities.responsibilities;
5
5
 
6
6
  import Enzyme, { mount } from 'enzyme';
@@ -0,0 +1,75 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import classNames from 'classnames';
4
+
5
+ export function YearOfBirth({
6
+ hasError = false,
7
+ customErrorMessage = '',
8
+ isDisabled = false,
9
+ isRequired = true,
10
+ value = '',
11
+ prompt = '',
12
+ fieldId = 'yearOfBirthField',
13
+ inputId = 'yearOfBirth',
14
+ inputName = 'yearOfBirth',
15
+ fieldLabel = 'Year of birth',
16
+ dataTrackable = 'year-of-birth',
17
+ }) {
18
+ const inputWrapperClassNames = classNames([
19
+ 'o-forms-input',
20
+ 'o-forms-input--text',
21
+ { 'o-forms-input--invalid': hasError },
22
+ ]);
23
+
24
+ const fieldTitleClassName = classNames([
25
+ 'o-forms-title',
26
+ { 'o-forms-field--optional': !isRequired },
27
+ ]);
28
+
29
+ return (
30
+ <label
31
+ id={fieldId}
32
+ htmlFor={inputId}
33
+ className="o-forms-field ncf__validation-error"
34
+ data-validate="required,number"
35
+ >
36
+ <span className={fieldTitleClassName}>
37
+ <span className="o-forms-title__main">{fieldLabel}</span>
38
+ <span className="o-forms-title__prompt">{prompt}</span>
39
+ </span>
40
+ <span className={inputWrapperClassNames}>
41
+ <input
42
+ type="number"
43
+ id={inputId}
44
+ name={inputName}
45
+ placeholder="YYYY"
46
+ autoComplete="bday-year"
47
+ minLength="4"
48
+ maxLength="4"
49
+ data-trackable={dataTrackable}
50
+ aria-required={isRequired}
51
+ required={isRequired}
52
+ disabled={isDisabled}
53
+ defaultValue={value}
54
+ />
55
+ <span className="o-forms-input__error">
56
+ {customErrorMessage || 'Please enter a valid year of birth'}
57
+ </span>
58
+ </span>
59
+ </label>
60
+ );
61
+ }
62
+
63
+ YearOfBirth.propTypes = {
64
+ hasError: PropTypes.bool,
65
+ customErrorMessage: PropTypes.string,
66
+ isDisabled: PropTypes.bool,
67
+ isRequired: PropTypes.bool,
68
+ value: PropTypes.string,
69
+ prompt: PropTypes.string,
70
+ fieldId: PropTypes.string,
71
+ inputId: PropTypes.string,
72
+ inputName: PropTypes.string,
73
+ fieldLabel: PropTypes.string,
74
+ dataTrackable: PropTypes.string,
75
+ };
@@ -0,0 +1,53 @@
1
+ import { YearOfBirth } from './index';
2
+ import { expectToRenderCorrectly } from '../test-jest/helpers/expect-to-render-correctly';
3
+
4
+ expect.extend(expectToRenderCorrectly);
5
+
6
+ describe('YearOfBirth', () => {
7
+ it('renders a year of birth input with default params', () => {
8
+ const props = {};
9
+
10
+ expect(YearOfBirth).toRenderCorrectly(props);
11
+ });
12
+
13
+ it(`renders a year of birth input with custom prompt`, () => {
14
+ const props = {
15
+ prompt: 'This helps us ensure people of all ages find value in the FT',
16
+ };
17
+
18
+ expect(YearOfBirth).toRenderCorrectly(props);
19
+ });
20
+
21
+ it(`renders a year of birth input with default error message`, () => {
22
+ const props = {
23
+ hasError: true,
24
+ };
25
+
26
+ expect(YearOfBirth).toRenderCorrectly(props);
27
+ });
28
+
29
+ it(`renders an optional year of birth input`, () => {
30
+ const props = {
31
+ isRequired: false,
32
+ };
33
+
34
+ expect(YearOfBirth).toRenderCorrectly(props);
35
+ });
36
+
37
+ it('render a year of birth input with default value', () => {
38
+ const props = {
39
+ value: '2001',
40
+ };
41
+
42
+ expect(YearOfBirth).toRenderCorrectly(props);
43
+ });
44
+
45
+ it('render a year of birth input with disabled field', () => {
46
+ const props = {
47
+ value: '2001',
48
+ isDisabled: true,
49
+ };
50
+
51
+ expect(YearOfBirth).toRenderCorrectly(props);
52
+ });
53
+ });
@@ -0,0 +1,15 @@
1
+ import React from 'react';
2
+ import { YearOfBirth } from './year-of-birth';
3
+
4
+ export default {
5
+ title: 'Year of Birth',
6
+ component: YearOfBirth,
7
+ };
8
+
9
+ export const Basic = (args) => <YearOfBirth {...args} />;
10
+ Basic.args = {
11
+ isRequired: true,
12
+ value: '1980',
13
+ prompt: 'This helps us ensure people of all ages find value in the FT',
14
+ fieldLabel: 'Year of birth',
15
+ };
package/dist/index.js CHANGED
@@ -357,6 +357,12 @@ Object.defineProperty(exports, "TrialBanner", {
357
357
  return _trialBanner.TrialBanner;
358
358
  }
359
359
  });
360
+ Object.defineProperty(exports, "YearOfBirth", {
361
+ enumerable: true,
362
+ get: function get() {
363
+ return _yearOfBirth.YearOfBirth;
364
+ }
365
+ });
360
366
  var _acceptTerms = require("./accept-terms");
361
367
  var _acceptTermsPrivacyPolicy = require("./accept-terms-privacy-policy");
362
368
  var _acceptTermsSubscription = require("./accept-terms-subscription");
@@ -415,4 +421,5 @@ var _educationJobTitle = require("./education-job-title");
415
421
  var _graduationDate = require("./graduation-date");
416
422
  var _googleSignIn = require("./google-sign-in");
417
423
  var _textInput = require("./text-input");
418
- var _deferredBillingTerms = require("./deferred-billing-terms");
424
+ var _deferredBillingTerms = require("./deferred-billing-terms");
425
+ var _yearOfBirth = require("./year-of-birth");
@@ -0,0 +1,82 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.YearOfBirth = YearOfBirth;
8
+ var _react = _interopRequireDefault(require("react"));
9
+ var _propTypes = _interopRequireDefault(require("prop-types"));
10
+ var _classnames = _interopRequireDefault(require("classnames"));
11
+ function YearOfBirth(_ref) {
12
+ var _ref$hasError = _ref.hasError,
13
+ hasError = _ref$hasError === void 0 ? false : _ref$hasError,
14
+ _ref$customErrorMessa = _ref.customErrorMessage,
15
+ customErrorMessage = _ref$customErrorMessa === void 0 ? '' : _ref$customErrorMessa,
16
+ _ref$isDisabled = _ref.isDisabled,
17
+ isDisabled = _ref$isDisabled === void 0 ? false : _ref$isDisabled,
18
+ _ref$isRequired = _ref.isRequired,
19
+ isRequired = _ref$isRequired === void 0 ? true : _ref$isRequired,
20
+ _ref$value = _ref.value,
21
+ value = _ref$value === void 0 ? '' : _ref$value,
22
+ _ref$prompt = _ref.prompt,
23
+ prompt = _ref$prompt === void 0 ? '' : _ref$prompt,
24
+ _ref$fieldId = _ref.fieldId,
25
+ fieldId = _ref$fieldId === void 0 ? 'yearOfBirthField' : _ref$fieldId,
26
+ _ref$inputId = _ref.inputId,
27
+ inputId = _ref$inputId === void 0 ? 'yearOfBirth' : _ref$inputId,
28
+ _ref$inputName = _ref.inputName,
29
+ inputName = _ref$inputName === void 0 ? 'yearOfBirth' : _ref$inputName,
30
+ _ref$fieldLabel = _ref.fieldLabel,
31
+ fieldLabel = _ref$fieldLabel === void 0 ? 'Year of birth' : _ref$fieldLabel,
32
+ _ref$dataTrackable = _ref.dataTrackable,
33
+ dataTrackable = _ref$dataTrackable === void 0 ? 'year-of-birth' : _ref$dataTrackable;
34
+ var inputWrapperClassNames = (0, _classnames["default"])(['o-forms-input', 'o-forms-input--text', {
35
+ 'o-forms-input--invalid': hasError
36
+ }]);
37
+ var fieldTitleClassName = (0, _classnames["default"])(['o-forms-title', {
38
+ 'o-forms-field--optional': !isRequired
39
+ }]);
40
+ return /*#__PURE__*/_react["default"].createElement("label", {
41
+ id: fieldId,
42
+ htmlFor: inputId,
43
+ className: "o-forms-field ncf__validation-error",
44
+ "data-validate": "required,number"
45
+ }, /*#__PURE__*/_react["default"].createElement("span", {
46
+ className: fieldTitleClassName
47
+ }, /*#__PURE__*/_react["default"].createElement("span", {
48
+ className: "o-forms-title__main"
49
+ }, fieldLabel), /*#__PURE__*/_react["default"].createElement("span", {
50
+ className: "o-forms-title__prompt"
51
+ }, prompt)), /*#__PURE__*/_react["default"].createElement("span", {
52
+ className: inputWrapperClassNames
53
+ }, /*#__PURE__*/_react["default"].createElement("input", {
54
+ type: "number",
55
+ id: inputId,
56
+ name: inputName,
57
+ placeholder: "YYYY",
58
+ autoComplete: "bday-year",
59
+ minLength: "4",
60
+ maxLength: "4",
61
+ "data-trackable": dataTrackable,
62
+ "aria-required": isRequired,
63
+ required: isRequired,
64
+ disabled: isDisabled,
65
+ defaultValue: value
66
+ }), /*#__PURE__*/_react["default"].createElement("span", {
67
+ className: "o-forms-input__error"
68
+ }, customErrorMessage || 'Please enter a valid year of birth')));
69
+ }
70
+ YearOfBirth.propTypes = {
71
+ hasError: _propTypes["default"].bool,
72
+ customErrorMessage: _propTypes["default"].string,
73
+ isDisabled: _propTypes["default"].bool,
74
+ isRequired: _propTypes["default"].bool,
75
+ value: _propTypes["default"].string,
76
+ prompt: _propTypes["default"].string,
77
+ fieldId: _propTypes["default"].string,
78
+ inputId: _propTypes["default"].string,
79
+ inputName: _propTypes["default"].string,
80
+ fieldLabel: _propTypes["default"].string,
81
+ dataTrackable: _propTypes["default"].string
82
+ };
@@ -1,3 +1,606 @@
1
- const { demographics } = require('n-common-static-data');
1
+ const demographics = {
2
+ industries: {
3
+ industries: [
4
+ {
5
+ description: 'Accountancy & tax advisory',
6
+ code: 'ACC',
7
+ status: 'active',
8
+ },
9
+ {
10
+ description: 'Banking',
11
+ code: 'BKG',
12
+ status: 'active',
13
+ },
14
+ {
15
+ description: 'Financial services',
16
+ code: 'FSE',
17
+ status: 'active',
18
+ },
19
+ {
20
+ description: 'Comms/Publishing/Media',
21
+ code: 'MAP',
22
+ status: 'active',
23
+ },
24
+ {
25
+ description: 'Govt/public service/NGO',
26
+ code: 'GPV',
27
+ status: 'active',
28
+ },
29
+ {
30
+ description: 'Education/Academia',
31
+ code: 'EDU',
32
+ status: 'active',
33
+ },
34
+ {
35
+ description: 'IT/computing',
36
+ code: 'IT',
37
+ status: 'active',
38
+ },
39
+ {
40
+ description: 'Telecommunications',
41
+ code: 'TEL',
42
+ status: 'active',
43
+ },
44
+ {
45
+ description: 'Industrial goods & services',
46
+ code: 'MAN',
47
+ status: 'active',
48
+ },
49
+ {
50
+ description: 'Consulting/business services',
51
+ code: 'BSE',
52
+ status: 'active',
53
+ },
54
+ {
55
+ description: 'Trade/import/export',
56
+ code: 'TIE',
57
+ status: 'active',
58
+ },
59
+ {
60
+ description: 'Chemicals',
61
+ code: 'PHC',
62
+ status: 'active',
63
+ },
64
+ {
65
+ description: 'Transport/logistics',
66
+ code: 'TRL',
67
+ status: 'active',
68
+ },
69
+ {
70
+ description: 'Travel & leisure',
71
+ code: 'TRV',
72
+ status: 'active',
73
+ },
74
+ {
75
+ description: 'Engineering/construction',
76
+ code: 'ENC',
77
+ status: 'active',
78
+ },
79
+ {
80
+ description: 'Oil/gas/mining',
81
+ code: 'OGM',
82
+ status: 'active',
83
+ },
84
+ {
85
+ description: 'Legal services',
86
+ code: 'LAW',
87
+ status: 'active',
88
+ },
89
+ {
90
+ description: 'Personal & household goods',
91
+ code: 'RET',
92
+ status: 'active',
93
+ },
94
+ {
95
+ description: 'N/A',
96
+ code: 'OTH',
97
+ status: 'active',
98
+ },
99
+ {
100
+ description: 'Property',
101
+ code: 'REA',
102
+ status: 'active',
103
+ },
104
+ {
105
+ description: 'IT/Tech/Telecoms',
106
+ code: 'ITT',
107
+ status: 'active',
108
+ },
109
+ {
110
+ description: 'Energy/utilities',
111
+ code: 'ENU',
112
+ status: 'active',
113
+ },
114
+ {
115
+ description: 'Aerospace & defence',
116
+ code: 'DEF',
117
+ status: 'active',
118
+ },
119
+ {
120
+ description: 'Automobiles',
121
+ code: 'CAR',
122
+ status: 'active',
123
+ },
124
+ {
125
+ description: 'Basic resources/mining',
126
+ code: 'RES',
127
+ status: 'active',
128
+ },
129
+ {
130
+ description: 'Health & pharmaceuticals',
131
+ code: 'HEA',
132
+ status: 'active',
133
+ },
134
+ {
135
+ description: 'Food & beverages',
136
+ code: 'FOO',
137
+ status: 'active',
138
+ },
139
+ {
140
+ description: 'Fund/asset management',
141
+ code: 'FAM',
142
+ status: 'active',
143
+ },
144
+ {
145
+ description: 'Insurance',
146
+ code: 'INS',
147
+ status: 'active',
148
+ },
149
+ {
150
+ description: 'Retail',
151
+ code: 'RTL',
152
+ status: 'active',
153
+ },
154
+ {
155
+ description: 'Apparel and Fashion',
156
+ code: 'APF',
157
+ status: 'active',
158
+ },
159
+ {
160
+ description: 'Biotechnology',
161
+ code: 'BIO',
162
+ status: 'active',
163
+ },
164
+ {
165
+ description: 'Capital Markets',
166
+ code: 'CAP',
167
+ status: 'active',
168
+ },
169
+ {
170
+ description: 'Computer Games',
171
+ code: 'GAM',
172
+ status: 'active',
173
+ },
174
+ {
175
+ description: 'Cosmetics',
176
+ code: 'COS',
177
+ status: 'active',
178
+ },
179
+ {
180
+ description: 'Design',
181
+ code: 'DES',
182
+ status: 'active',
183
+ },
184
+ {
185
+ description: 'Electrical/Electronic Manufacturing',
186
+ code: 'ELE',
187
+ status: 'active',
188
+ },
189
+ {
190
+ description: 'Environmental Services',
191
+ code: 'ENV',
192
+ status: 'active',
193
+ },
194
+ {
195
+ description: 'Executive Office',
196
+ code: 'EXE',
197
+ status: 'active',
198
+ },
199
+ {
200
+ description: 'Hospitality',
201
+ code: 'HOS',
202
+ status: 'active',
203
+ },
204
+ {
205
+ description: 'Import and Export',
206
+ code: 'IAE',
207
+ status: 'active',
208
+ },
209
+ {
210
+ description: 'Investment banking',
211
+ code: 'INV',
212
+ status: 'active',
213
+ },
214
+ {
215
+ description: 'Luxury goods and jewelry',
216
+ code: 'LUX',
217
+ status: 'active',
218
+ },
219
+ {
220
+ description: 'Management consultancy',
221
+ code: 'MNG',
222
+ status: 'active',
223
+ },
224
+ {
225
+ description: 'Mental Healthcare',
226
+ code: 'MEN',
227
+ status: 'active',
228
+ },
229
+ {
230
+ description: 'Mining and Metals',
231
+ code: 'MET',
232
+ status: 'active',
233
+ },
234
+ {
235
+ description: 'Outsourcing/Offshoring',
236
+ code: 'OUT',
237
+ status: 'active',
238
+ },
239
+ {
240
+ description: 'Philanthropy',
241
+ code: 'PHL',
242
+ status: 'active',
243
+ },
244
+ {
245
+ description: 'Plastics',
246
+ code: 'PLA',
247
+ status: 'active',
248
+ },
249
+ {
250
+ description: 'Political organisation',
251
+ code: 'POL',
252
+ status: 'active',
253
+ },
254
+ {
255
+ description: 'Public Policy',
256
+ code: 'PUB',
257
+ status: 'active',
258
+ },
259
+ {
260
+ description: 'Public Relations and communications',
261
+ code: 'REL',
262
+ status: 'active',
263
+ },
264
+ {
265
+ description: 'Renewables and environment',
266
+ code: 'REN',
267
+ status: 'active',
268
+ },
269
+ {
270
+ description: 'Shipbuilding',
271
+ code: 'SHI',
272
+ status: 'active',
273
+ },
274
+ {
275
+ description: 'Sporting Goods',
276
+ code: 'SPG',
277
+ status: 'active',
278
+ },
279
+ {
280
+ description: 'Sports',
281
+ code: 'SPO',
282
+ status: 'active',
283
+ },
284
+ {
285
+ description: 'Staffing and recruiting',
286
+ code: 'STA',
287
+ status: 'active',
288
+ },
289
+ {
290
+ description: 'Tobacco',
291
+ code: 'TOB',
292
+ status: 'active',
293
+ },
294
+ {
295
+ description: 'Venture capital and private equity',
296
+ code: 'VEN',
297
+ status: 'active',
298
+ },
299
+ ],
300
+ },
301
+ positions: {
302
+ positions: [
303
+ {
304
+ description: 'Academic',
305
+ code: 'BU',
306
+ status: 'active',
307
+ },
308
+ {
309
+ description: 'Analyst',
310
+ code: 'AN',
311
+ status: 'active',
312
+ },
313
+ {
314
+ description: 'Consultant',
315
+ code: 'CS',
316
+ status: 'active',
317
+ },
318
+ {
319
+ description: 'Associate',
320
+ code: 'AS',
321
+ status: 'active',
322
+ },
323
+ {
324
+ description: 'Broker/Trader/Advisor',
325
+ code: 'BR',
326
+ status: 'active',
327
+ },
328
+ {
329
+ description: 'CEO/President/Chairperson',
330
+ code: 'CP',
331
+ status: 'active',
332
+ },
333
+ {
334
+ description: 'C-Suite',
335
+ code: 'CL',
336
+ status: 'active',
337
+ },
338
+ {
339
+ description: 'Director',
340
+ code: 'DR',
341
+ status: 'active',
342
+ },
343
+ {
344
+ description: 'Diplomat/Sen Govt Officer',
345
+ code: 'GI',
346
+ status: 'active',
347
+ },
348
+ {
349
+ description: 'Intern/Trainee/Student',
350
+ code: 'IN',
351
+ status: 'active',
352
+ },
353
+ {
354
+ description: 'Manager/Supervisor',
355
+ code: 'MA',
356
+ status: 'active',
357
+ },
358
+ {
359
+ description: 'MBA Student',
360
+ code: 'MB',
361
+ status: 'active',
362
+ },
363
+ {
364
+ description: 'Not working',
365
+ code: 'NW',
366
+ status: 'active',
367
+ },
368
+ {
369
+ description: 'Executive Management',
370
+ code: 'OE',
371
+ status: 'active',
372
+ },
373
+ {
374
+ description: 'Other',
375
+ code: 'OT',
376
+ status: 'active',
377
+ },
378
+ {
379
+ description: 'Founder/Owner',
380
+ code: 'OP',
381
+ status: 'active',
382
+ },
383
+ {
384
+ description: 'Partner/Principal',
385
+ code: 'PA',
386
+ status: 'active',
387
+ },
388
+ {
389
+ description: 'Programme/Project Manager',
390
+ code: 'PM',
391
+ status: 'active',
392
+ },
393
+ {
394
+ description: 'Politician/Government Minister',
395
+ code: 'PO',
396
+ status: 'active',
397
+ },
398
+ {
399
+ description: 'Senior Manager/Dept Head',
400
+ code: 'SM',
401
+ status: 'active',
402
+ },
403
+ {
404
+ description: 'Technical/Business Specialist',
405
+ code: 'TS',
406
+ status: 'active',
407
+ },
408
+ {
409
+ description: 'Vice President',
410
+ code: 'VP',
411
+ status: 'active',
412
+ },
413
+ ],
414
+ },
415
+ responsibilities: {
416
+ responsibilities: [
417
+ {
418
+ description: 'Finance',
419
+ code: 'FIN',
420
+ status: 'active',
421
+ },
422
+ {
423
+ description: 'Marketing',
424
+ code: 'MKT',
425
+ status: 'active',
426
+ },
427
+ {
428
+ description: 'Strategy',
429
+ code: 'CPL',
430
+ status: 'active',
431
+ },
432
+ {
433
+ description: 'Legal',
434
+ code: 'LEG',
435
+ status: 'active',
436
+ },
437
+ {
438
+ description: 'Human resources',
439
+ code: 'PAT',
440
+ status: 'active',
441
+ },
442
+ {
443
+ description: 'Administration',
444
+ code: 'ADL',
445
+ status: 'active',
446
+ },
447
+ {
448
+ description: 'Financial Advisor',
449
+ code: 'IFA',
450
+ status: 'active',
451
+ },
452
+ {
453
+ description: 'Data & Analytics',
454
+ code: 'RAD',
455
+ status: 'active',
456
+ },
457
+ {
458
+ description: 'Sales & business development',
459
+ code: 'SAL',
460
+ status: 'active',
461
+ },
462
+ {
463
+ description: 'Not working',
464
+ code: 'NOT',
465
+ status: 'active',
466
+ },
467
+ {
468
+ description: 'Portfolio management',
469
+ code: 'MON',
470
+ status: 'active',
471
+ },
472
+ {
473
+ description: 'Investments',
474
+ code: 'PRI',
475
+ status: 'active',
476
+ },
477
+ {
478
+ description: 'Student',
479
+ code: 'STU',
480
+ status: 'active',
481
+ },
482
+ {
483
+ description: 'Information technology',
484
+ code: 'TEC',
485
+ status: 'active',
486
+ },
487
+ {
488
+ description: 'Purchasing & procurement',
489
+ code: 'BUY',
490
+ status: 'active',
491
+ },
492
+ {
493
+ description: 'Knowledge & information management',
494
+ code: 'KNO',
495
+ status: 'active',
496
+ },
497
+ {
498
+ description: 'Operations',
499
+ code: 'OPS',
500
+ status: 'active',
501
+ },
502
+ {
503
+ description: 'Product management',
504
+ code: 'PRO',
505
+ status: 'active',
506
+ },
507
+ {
508
+ description: 'Risk & compliance',
509
+ code: 'RSK',
510
+ status: 'active',
511
+ },
512
+ {
513
+ description: 'Accounting',
514
+ code: 'ACC',
515
+ status: 'active',
516
+ },
517
+ {
518
+ description: 'Arts & design',
519
+ code: 'ART',
520
+ status: 'active',
521
+ },
522
+ {
523
+ description: 'Community & social services',
524
+ code: 'COM',
525
+ status: 'active',
526
+ },
527
+ {
528
+ description: 'Consulting',
529
+ code: 'COS',
530
+ status: 'active',
531
+ },
532
+ {
533
+ description: 'Engineering',
534
+ code: 'ENG',
535
+ status: 'active',
536
+ },
537
+ {
538
+ description: 'Facilities',
539
+ code: 'FAC',
540
+ status: 'active',
541
+ },
542
+ {
543
+ description: 'Healthcare services',
544
+ code: 'HEA',
545
+ status: 'active',
546
+ },
547
+ {
548
+ description: 'Media & communication',
549
+ code: 'MED',
550
+ status: 'active',
551
+ },
552
+ {
553
+ description: 'Program and project management',
554
+ code: 'PMN',
555
+ status: 'active',
556
+ },
557
+ {
558
+ description: 'Real Estate',
559
+ code: 'REA',
560
+ status: 'active',
561
+ },
562
+ {
563
+ description: 'Research',
564
+ code: 'RSR',
565
+ status: 'active',
566
+ },
567
+ {
568
+ description: 'Support',
569
+ code: 'SUP',
570
+ status: 'active',
571
+ },
572
+ {
573
+ description: 'Teaching & training',
574
+ code: 'TEA',
575
+ status: 'active',
576
+ },
577
+ {
578
+ description: 'ESG (Env’tl, Social & Corp. Governance)',
579
+ code: 'ESG',
580
+ status: 'active',
581
+ },
582
+ {
583
+ description: 'Fund management',
584
+ code: 'FUM',
585
+ status: 'active',
586
+ },
587
+ {
588
+ description: 'Learning & Development',
589
+ code: 'LEA',
590
+ status: 'active',
591
+ },
592
+ {
593
+ description: 'Policy making',
594
+ code: 'POL',
595
+ status: 'active',
596
+ },
597
+ {
598
+ description: 'Quantitative Analysis',
599
+ code: 'QUA',
600
+ status: 'active',
601
+ },
602
+ ],
603
+ },
604
+ };
2
605
 
3
606
  module.exports = { demographics };
package/helpers/index.js CHANGED
@@ -13,6 +13,5 @@ module.exports = {
13
13
  .supportedPostcodeValidators,
14
14
  allSupportedPostcodeExamples: require('./supportedPostcodeExamples')
15
15
  .allSupportedPostcodeExamples,
16
- demographics: require('./demographics').demographics,
17
16
  billingCountries: require('./billing-countries').billingCountries,
18
17
  };
@@ -14,11 +14,6 @@ describe('helpers', () => {
14
14
  expect(helpers['billingCountries']).toHaveProperty('countries');
15
15
  });
16
16
 
17
- it('export demographics', () => {
18
- expect(helpers).toHaveProperty('demographics');
19
- expect(helpers['demographics']).toHaveProperty('industries');
20
- });
21
-
22
17
  it('export cemeaV1ISO', () => {
23
18
  expect(helpers).toHaveProperty('cemeaV1ISO');
24
19
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@financial-times/n-conversion-forms",
3
- "version": "41.0.4",
3
+ "version": "41.1.0",
4
4
  "description": "Containing jsx components and styles for forms included on Accounts and Acquisition apps (next-signup, next-profile, next-retention, etc).",
5
5
  "main": "./dist/index.js",
6
6
  "scripts": {
@@ -13,7 +13,11 @@ class Validation {
13
13
  * @param {boolean} [mutePromptBeforeLeaving=false] (default: false) Whether to prompt the user before leaving if there have been changes in any of the fields.
14
14
  * @param {FormsOptions} [formsOptions={}] Options object for Origami Forms: https://github.com/Financial-Times/origami/blob/main/components/o-forms/src/js/forms.js
15
15
  */
16
- constructor({ mutePromptBeforeLeaving, formsOptions } = {}) {
16
+ constructor({
17
+ mutePromptBeforeLeaving,
18
+ formsOptions,
19
+ debounceInterval = 1500,
20
+ } = {}) {
17
21
  this.$form = document.querySelector('form.ncf');
18
22
  this.oForms = OForms.init(this.$form, formsOptions);
19
23
  this.$requiredEls = this.oForms.formInputs.filter(
@@ -24,6 +28,7 @@ class Validation {
24
28
  this.formSubmit = false;
25
29
  this.mutePromptBeforeLeaving = mutePromptBeforeLeaving || false;
26
30
  this.customValidation = new Map();
31
+ this.debounceInterval = debounceInterval;
27
32
  }
28
33
 
29
34
  /**
@@ -78,7 +83,7 @@ class Validation {
78
83
  *
79
84
  * @param {DOMElement} field The field the custom validation is being added on to.
80
85
  * @param {Function} validator The function that will be run to determine whether the field is valid (needs to return `true` or `false`).
81
- * @param {String} errorMessage The error message to display to the user should the validation fail.
86
+ * @param {String | Function} errorMessage The error message to display to the user should the validation fail.
82
87
  */
83
88
  addCustomValidation({ field, validator, errorMessage }) {
84
89
  if (this.customValidation.get(field.name)) {
@@ -91,7 +96,10 @@ class Validation {
91
96
 
92
97
  $message.id = id;
93
98
  $message.className = 'o-forms-input__error ncf__custom-validation-error';
94
- $message.innerHTML = errorMessage;
99
+ $message.innerHTML =
100
+ typeof errorMessage === 'function'
101
+ ? await errorMessage()
102
+ : errorMessage;
95
103
 
96
104
  const isValid = await validator();
97
105
  if (!isValid) {
@@ -116,25 +124,34 @@ class Validation {
116
124
  $field.setCustomValidity($message);
117
125
 
118
126
  const $parent = $field.parentNode;
119
- const $oFormsErrorText = $parent.querySelector('.o-forms-input__error');
127
+ // Get the default o-forms error element that is not created by the custom validtion (if any)
128
+ // so we can hide it and show our custom error message.
129
+ const $oFormsErrorText = $parent.querySelector(
130
+ ':not(.ncf__custom-validation-error).o-forms-input__error'
131
+ );
120
132
 
133
+ // Highlight the field with red outline
121
134
  $parent.classList.remove('o-forms-input--valid');
122
135
  $parent.classList.add('o-forms-input--invalid');
123
136
 
124
- if (!document.querySelector(`#custom-validation-for-${$field.name}`)) {
137
+ // Get the existing custom error element
138
+ // If it doesn't exist, append the new error element to the input field
139
+ // Otherwise, update the error message
140
+ const existingErrorEl = document.querySelector(
141
+ `#custom-validation-for-${$field.name}`
142
+ );
143
+ if (!existingErrorEl) {
125
144
  // In order for this error to hang around after normal oForms validation happens it
126
- // needs to exist outside the context of the field.
145
+ // needs to exist outside the context of the field.
127
146
  $parent.insertBefore($message, $field.nextSibling);
147
+ } else {
148
+ // else modify the error message inside the existing customer error element
149
+ existingErrorEl.innerHTML = $message.innerHTML;
128
150
  }
129
151
 
130
- if (
131
- $oFormsErrorText &&
132
- $oFormsErrorText.parentNode.className.indexOf(
133
- 'ncf__custom-validation-error'
134
- ) === -1
135
- ) {
136
- // If there's an oForms error we need to hide it so that we can use the `o-forms-input--invalid` class
137
- // on the container to highlight the field as invalid.
152
+ // If there's an oForms error we need to hide it so that we can use the `o-forms-input--invalid` class
153
+ // on the container to highlight the field as invalid.
154
+ if ($oFormsErrorText) {
138
155
  $oFormsErrorText.style.display = 'none';
139
156
  }
140
157
  }
@@ -150,12 +167,15 @@ class Validation {
150
167
  `#custom-validation-for-${$field.name}`
151
168
  );
152
169
  const $oFormsErrorText = $field.parentNode.querySelector(
153
- '.o-forms-input__error'
170
+ ':not(.ncf__custom-validation-error).o-forms-input__error'
154
171
  );
155
172
 
173
+ // Remove the custom error element
156
174
  if ($message) {
157
175
  $message.parentNode.removeChild($message);
158
176
  }
177
+ // Reset the display style of the default oForms error element
178
+ // so it can be toggled by oForms
159
179
  if ($oFormsErrorText) {
160
180
  $oFormsErrorText.style.display = null;
161
181
  }
@@ -180,7 +200,7 @@ class Validation {
180
200
 
181
201
  setTimeout(() => {
182
202
  delete this.debounceCustomValidation;
183
- }, 1500);
203
+ }, this.debounceInterval);
184
204
  }
185
205
 
186
206
  return !document.querySelector('.ncf__custom-validation-error');
@@ -0,0 +1,24 @@
1
+ class YearOfBirth {
2
+ constructor(element) {
3
+ if (!element) {
4
+ throw new Error('Please supply a DOM element');
5
+ }
6
+
7
+ this.element = element;
8
+ this.$yearOfBirth = element.querySelector('.ncf #yearOfBirth');
9
+
10
+ if (!this.$yearOfBirth) {
11
+ throw new Error('Please include the year of birth partial on the page');
12
+ }
13
+ }
14
+
15
+ handleYearOfBirthBlur(callback = () => {}) {
16
+ return this.$yearOfBirth.addEventListener('blur', callback);
17
+ }
18
+
19
+ getYearOfBirth() {
20
+ return this.$yearOfBirth.value;
21
+ }
22
+ }
23
+
24
+ module.exports = YearOfBirth;