@financial-times/n-myft-ui 23.1.3 → 25.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. package/.circleci/config.yml +27 -30
  2. package/.circleci/shared-helpers/helper-npm-install-peer-deps +6 -5
  3. package/.github/settings.yml +1 -1
  4. package/.scss-lint.yml +3 -3
  5. package/Makefile +1 -0
  6. package/README.md +62 -8
  7. package/build-state/npm-shrinkwrap.json +49383 -17508
  8. package/components/collections/collections.jsx +68 -0
  9. package/components/collections/collections.test.js +83 -0
  10. package/components/concept-list/concept-list.jsx +55 -0
  11. package/components/concept-list/concept-list.test.js +116 -0
  12. package/components/csrf-token/__tests__/input.test.js +23 -0
  13. package/components/csrf-token/input.jsx +26 -0
  14. package/components/follow-button/__tests__/follow-button.test.js +40 -0
  15. package/components/follow-button/follow-button.jsx +174 -0
  16. package/components/index.js +15 -0
  17. package/components/instant-alert/instant-alert.html +1 -1
  18. package/components/pin-button/pin-button.jsx +40 -0
  19. package/components/pin-button/pin-button.test.js +57 -0
  20. package/components/save-for-later/save-for-later.jsx +103 -0
  21. package/components/save-for-later/save-for-later.test.js +59 -0
  22. package/components/unread-articles-indicator/date-fns.js +5 -12
  23. package/demos/app.js +39 -21
  24. package/demos/templates/demo-layout.html +1 -1
  25. package/demos/templates/demo.html +436 -415
  26. package/demos/templates/demo.jsx +125 -0
  27. package/dist/bundles/bundle.js +3133 -0
  28. package/jest.config.js +8 -0
  29. package/package.json +38 -13
  30. package/webpack.config.js +34 -0
  31. package/components/collections/collections.html +0 -85
  32. package/components/concept-list/concept-list.html +0 -31
  33. package/components/csrf-token/input.html +0 -5
  34. package/components/follow-button/follow-button.html +0 -79
  35. package/components/pin-button/pin-button.html +0 -20
  36. package/components/save-for-later/save-for-later.html +0 -67
  37. package/demos/fixtures/follow-button-plus-digest.json +0 -6
  38. package/demos/templates/digest-on-follow.html +0 -12
package/jest.config.js ADDED
@@ -0,0 +1,8 @@
1
+ module.exports = {
2
+ transform: {
3
+ '.(js|jsx)': '@sucrase/jest-plugin',
4
+ },
5
+ testEnvironment: 'jest-environment-jsdom',
6
+ modulePathIgnorePatterns: ['node_modules', 'bower_components'],
7
+ testMatch: ['<rootDir>/components/**/*.test.js']
8
+ };
package/package.json CHANGED
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "@financial-times/n-myft-ui",
3
- "version": "23.1.3",
3
+ "version": "25.0.0",
4
4
  "description": "Client side component for interaction with myft",
5
- "main": "server.js",
5
+ "main": "dist/bundles/bundle.js",
6
+ "module": "dist/bundles/bundle.js",
6
7
  "scripts": {
7
8
  "test": "echo \"Error: no test specified\" && exit 1",
8
9
  "commit": "commit-wizard",
9
- "precommit": "node_modules/.bin/secret-squirrel",
10
- "prepush": "make verify -j3",
11
- "commitmsg": "node_modules/.bin/secret-squirrel-commitmsg",
12
- "prepare": "npx snyk protect || npx snyk protect -d || true"
10
+ "prepare": "npx snyk protect || npx snyk protect -d || true",
11
+ "preinstall": "npm_config_yes=true npx check-engine",
12
+ "build-package": "webpack"
13
13
  },
14
14
  "repository": {
15
15
  "type": "git",
@@ -27,10 +27,13 @@
27
27
  "@financial-times/dotcom-build-js": "0.4.1",
28
28
  "@financial-times/dotcom-build-sass": "0.4.1",
29
29
  "@financial-times/dotcom-page-kit-cli": "0.4.1",
30
- "@financial-times/n-gage": "^3.12.0",
31
- "@financial-times/n-heroku-tools": "8.3.1",
32
- "@financial-times/n-internal-tool": "2.3.4",
33
- "@financial-times/x-handlebars": "1.0.0-beta.21",
30
+ "@financial-times/dotcom-server-handlebars": "^3.0.0",
31
+ "@financial-times/dotcom-server-react-jsx": "^2.6.2",
32
+ "@financial-times/n-express": "^22.4.1",
33
+ "@financial-times/n-gage": "^8.3.2",
34
+ "@sucrase/jest-plugin": "^2.2.0",
35
+ "@testing-library/jest-dom": "^5.16.1",
36
+ "@testing-library/react": "^12.1.2",
34
37
  "ascii-table": "0.0.9",
35
38
  "autoprefixer": "9.7.0",
36
39
  "aws-sdk-mock": "4.5.0",
@@ -43,6 +46,8 @@
43
46
  "babel-plugin-transform-runtime": "^6.9.0",
44
47
  "babel-preset-env": "^1.7.0",
45
48
  "babel-preset-es2015": "^6.6.0",
49
+ "babel-preset-react": "^6.24.1",
50
+ "babel-preset-stage-2": "^6.24.1",
46
51
  "babel-runtime": "^6.9.2",
47
52
  "bower": "^1.8.8",
48
53
  "bower-resolve-webpack-plugin": "^1.0.5",
@@ -50,9 +55,11 @@
50
55
  "brotli": "^1.3.1",
51
56
  "chai": "4.2.0",
52
57
  "chalk": "2.4.2",
58
+ "check-engine": "^1.10.1",
53
59
  "css-loader": "^0.23.1",
54
60
  "denodeify": "^1.2.1",
55
61
  "eslint": "6.5.1",
62
+ "eslint-plugin-react": "^7.27.1",
56
63
  "extract-css-block-webpack-plugin": "^1.3.0",
57
64
  "extract-text-webpack-plugin": "3.0.2",
58
65
  "fetch-mock": "^5.0.3",
@@ -62,6 +69,8 @@
62
69
  "hyperons": "^0.4.1",
63
70
  "imports-loader": "0.8.0",
64
71
  "inject-loader": "^3.0.0",
72
+ "jest": "^27.4.5",
73
+ "jsdom": "^19.0.0",
65
74
  "karma": "4.4.1",
66
75
  "karma-browserstack-launcher": "1.5.1",
67
76
  "karma-chai": "^0.1.0",
@@ -78,21 +87,37 @@
78
87
  "mocha": "6.2.2",
79
88
  "mockery": "2.1.0",
80
89
  "node-fetch": "2.6.0",
81
- "node-sass": "4.13.0",
90
+ "node-sass": "^4.14.1",
82
91
  "nodemon": "^1.9.2",
83
92
  "npm-prepublish": "^1.2.1",
84
93
  "pa11y-ci": "^2.1.1",
85
94
  "postcss-loader": "^0.9.1",
95
+ "react": "^17.0.2",
86
96
  "regenerator-runtime": "^0.13.3",
87
- "sass-loader": "^3.2.0",
88
97
  "semver": "6.3.0",
89
98
  "sinon": "^7.1.0",
90
99
  "sinon-chai": "^3.2.0",
91
- "snyk": "^1.216.5"
100
+ "snyk": "^1.216.5",
101
+ "sucrase": "^3.10.1"
102
+ },
103
+ "volta": {
104
+ "node": "12.22.5",
105
+ "npm": "7.20.2"
106
+ },
107
+ "engines": {
108
+ "node": "12.x",
109
+ "npm": "7.x || 8.x"
92
110
  },
93
111
  "x-dash": {
94
112
  "engine": {
95
113
  "server": "hyperons"
96
114
  }
115
+ },
116
+ "husky": {
117
+ "hooks": {
118
+ "commit-msg": "node_modules/.bin/secret-squirrel-commitmsg",
119
+ "pre-commit": "node_modules/.bin/secret-squirrel",
120
+ "pre-push": "make verify -j3"
121
+ }
97
122
  }
98
123
  }
@@ -0,0 +1,34 @@
1
+ const path = require('path');
2
+
3
+ module.exports = {
4
+ entry: './components/index.js',
5
+ resolve: {
6
+ extensions: ['.js', '.jsx']
7
+ },
8
+ module: {
9
+ rules: [
10
+ {
11
+ test: /\.(js|jsx)$/,
12
+ exclude: /node_modules/,
13
+ use: [
14
+ {
15
+ loader: 'babel-loader',
16
+ options: {
17
+ presets: [
18
+ 'react',
19
+ 'stage-2'
20
+ ]
21
+ }
22
+ }
23
+ ]
24
+ }
25
+ ]
26
+ },
27
+ target: 'node',
28
+ output: {
29
+ path: path.resolve(__dirname, 'dist/bundles'),
30
+ filename: 'bundle.js',
31
+ libraryTarget: 'umd',
32
+ library: '@financial-times/n-myft-ui'
33
+ }
34
+ };
@@ -1,85 +0,0 @@
1
- <section
2
- class="collection {{#if liteStyle}}collection--lite{{else}}collection--regular{{/if}}"
3
- data-trackable="{{#if trackable}}{{trackable}}{{else}}collection{{/if}}">
4
- <header class="collection__header {{#if liteStyle}}collection__header--lite{{else}}collection__header--regular{{/if}}">
5
- <h2 class="collection__title {{#if liteStyle}}collection__title--lite{{else}}collection__title--regular{{/if}}">
6
- {{title}}
7
- </h2>
8
- </header>
9
- <ul class="collection__concepts">
10
- {{#concepts}}
11
- <li class="collection__concept">
12
- {{#if ../liteStyle}}
13
- {{> n-myft-ui/components/follow-button/follow-button
14
- variant="primary"
15
- buttonText=name
16
- collectionName=../collectionName
17
- }}
18
- {{else}}
19
- {{> n-myft-ui/components/follow-button/follow-button
20
- variant="inverse"
21
- buttonText=name
22
- collectionName=../collectionName
23
- }}
24
- {{/if}}
25
- </li>
26
- {{/concepts}}
27
- </ul>
28
- <div class="collection__meta">
29
- <form
30
- method="POST"
31
- action="#"
32
- data-myft-ui="follow"
33
- {{#if collectionName}}data-myft-tracking="collectionName={{collectionName}}"{{/if}}
34
- data-concept-id="
35
- {{~#concepts~}}
36
- {{conceptId}}
37
- {{~#unless @last~}}
38
- ,
39
- {{~/unless~}}
40
- {{~/concepts~}}"
41
- class="n-myft-ui n-myft-ui--follow n-ui-hide-core collection-follow-all">
42
- <input
43
- type="hidden"
44
- name="directType"
45
- value="
46
- {{~#concepts~}}
47
- {{directType}}
48
- {{~#unless @last~}}
49
- ,
50
- {{~/unless~}}
51
- {{~/concepts~}}"
52
- />
53
- {{> n-myft-ui/components/csrf-token/input}}
54
- <input
55
- type="hidden"
56
- name="name"
57
- value="
58
- {{~#concepts~}}
59
- {{name}}
60
- {{~#unless @last~}}
61
- ,
62
- {{~/unless~}}
63
- {{~/concepts~}}"
64
- />
65
- <button
66
- type="submit"
67
- aria-pressed="false"
68
- class="collection-follow-all__button {{#if liteStyle}}collection-follow-all__button--lite{{else}}collection-follow-all__button--regular{{/if}}"
69
- data-trackable="follow all"
70
- data-concept-id="
71
- {{~#concepts~}}
72
- {{conceptId}}
73
- {{~#unless @last~}}
74
- ,
75
- {{~/unless~}}
76
- {{~/concepts~}}"
77
- aria-label="Add all topics in the {{title}} collection to my F T"
78
- data-alternate-label="Remove all topics in the {{title}} collection from my F T"
79
- data-alternate-text="Added"
80
- title="Add all topics in the {{title}} collection to my F T">
81
- Add all to myFT
82
- </button>
83
- </form>
84
- </div>
85
- </section>
@@ -1,31 +0,0 @@
1
- {{#ifAll @root.flags.myFtApi @root.flags.myFtApiWrite concepts concepts.length}}
2
- <div
3
- class="concept-list"
4
- data-trackable="{{#if trackable}}{{trackable}}{{else}}concept-list{{/if}}">
5
- {{#ifSome contentType conceptListTitle}}
6
- <h2 class="concept-list__title">
7
- {{#if conceptListTitle}}
8
- {{conceptListTitle}}
9
- {{else}}
10
- Follow the topics in this {{contentType}}
11
- {{/if}}
12
- </h2>
13
- {{/ifSome}}
14
- <ul class="concept-list__list">
15
- {{#each concepts}}
16
- <li class="concept-list__list-item">
17
- <a
18
- href="{{relativeUrl}}"
19
- data-trackable="{{#if conceptTrackable}}{{conceptTrackable}}{{else}}concept{{/if}}"
20
- class="concept-list__concept">
21
- {{prefLabel}}
22
- </a>
23
- {{> n-myft-ui/components/follow-button/follow-button
24
- conceptId=id
25
- name=prefLabel
26
- }}
27
- </li>
28
- {{/each}}
29
- </ul>
30
- </div>
31
- {{/ifAll}}
@@ -1,5 +0,0 @@
1
- <input
2
- data-myft-csrf-token
3
- value="{{#if @root.cacheablePersonalisedUrl}}{{@root.csrfToken}}{{/if}}"
4
- type="hidden"
5
- name="token">
@@ -1,79 +0,0 @@
1
- {{#if @root.flags.myFtApiWrite}}
2
- <form
3
- class="n-myft-ui n-myft-ui--follow {{extraClasses}}"
4
- method="GET"
5
- data-myft-ui="follow"
6
- data-concept-id="{{conceptId}}"
7
- {{#if collectionName}}data-myft-tracking="collectionName={{collectionName}}"{{/if}}
8
- {{#if followPlusDigestEmail}}
9
- action="/__myft/api/core/follow-plus-digest-email/{{conceptId}}?method=put"
10
- data-myft-ui-variant="followPlusDigestEmail"
11
- {{else}}
12
- {{#ifAll setFollowButtonStateToSelected @root.cacheablePersonalisedUrl}}
13
- action="/myft/remove/{{conceptId}}"
14
- data-js-action="/__myft/api/core/followed/concept/{{conceptId}}?method=delete"
15
- {{else}}
16
- action="/myft/add/{{conceptId}}"
17
- data-js-action="/__myft/api/core/followed/concept/{{conceptId}}?method=put"
18
- {{/ifAll}}
19
- {{/if}}>
20
- {{> n-myft-ui/components/csrf-token/input}}
21
- <div
22
- class="n-myft-ui__announcement o-normalise-visually-hidden"
23
- aria-live="assertive"
24
- data-pressed-text="Now following {{name}}."
25
- data-unpressed-text="No longer following {{name}}."
26
- ></div>
27
- <button
28
- {{#ifAll setFollowButtonStateToSelected @root.cacheablePersonalisedUrl}}
29
- aria-label="Remove {{name}} from myFT"
30
- title="Remove {{name}} from myFT"
31
- data-alternate-label="Add {{name}} to myFT"
32
- aria-pressed="true"
33
- {{#if alternateText}}
34
- data-alternate-text="{{alternateText}}"
35
- {{else}}
36
- {{#if buttonText}}
37
- data-alternate-text="{{buttonText}}"
38
- {{else}}
39
- data-alternate-text="Add to myFT"
40
- {{/if}}
41
- {{/if}}
42
- {{else}}
43
- aria-pressed="false"
44
- aria-label="Add {{name}} to myFT"
45
- title="Add {{name}} to myFT"
46
- data-alternate-label="Remove {{name}} from myFT"
47
- {{#if alternateText}}
48
- data-alternate-text="{{alternateText}}"
49
- {{else}}
50
- {{#if buttonText}}
51
- data-alternate-text="{{buttonText}}"
52
- {{else}}
53
- data-alternate-text="Added"
54
- {{/if}}
55
- {{/if}}
56
- {{/ifAll}}
57
- class="{{extraButtonClasses}}
58
- n-myft-follow-button
59
- {{~#variant}} n-myft-follow-button--{{this}}{{/variant~}}"
60
- data-concept-id="{{conceptId}}" {{! duplicated here for tracking}}
61
- {{#if followPlusDigestEmail}}
62
- data-trackable-context-messaging="add-to-myft-plus-digest-button"
63
- {{/if}}
64
- data-trackable="follow"
65
- type="submit">
66
- {{~#if buttonText~}}
67
- {{buttonText}}
68
- {{~else~}}
69
- {{~#ifAll setFollowButtonStateToSelected @root.cacheablePersonalisedUrl~}}
70
- Added
71
- {{~else~}}
72
- Add to myFT
73
- {{~/ifAll~}}
74
- {{~/if~}}
75
- </button>
76
- </form>
77
- {{else}}
78
- <!-- Add to myFT button hidden due to myFtApiWrite being off -->
79
- {{/if}}
@@ -1,20 +0,0 @@
1
- {{#if showPrioritiseButton}}
2
- <span class="myft-pin-divider"></span>
3
- <div class="myft-pin-button-wrapper">
4
- <form method="post" action="/__myft/api/core/prioritised/concept/{{id}}?method={{#if prioritised}}delete{{else}}put{{/if}}" data-myft-prioritise>
5
- {{> n-myft-ui/components/csrf-token/input }}
6
- <input type="hidden" value="{{name}}" name="name"> {{#if directType}}
7
- <input type="hidden" value="{{directType}}" name="directType"> {{else}}
8
- <input type="hidden" value="http://www.ft.com/ontology/concept/Concept" name="directType"> {{/if}}
9
- <div
10
- class="n-myft-ui__announcement o-normalise-visually-hidden"
11
- aria-live="assertive"
12
- data-pressed-text="{{name}} pinned in myFT."
13
- data-unpressed-text="Unpinned {{name}} from myFT."
14
- ></div>
15
- <button id="myft-pin-button__{{id}}" class="myft-pin-button" data-prioritise-button data-trackable="prioritised" data-concept-id="{{id}}" data-prioritised="{{#if prioritised}}true{{else}}false{{/if}}"
16
- aria-label="{{#if prioritised}}Unpin{{else}}Pin{{/if}} {{name}} {{#if prioritised}}from{{else}}in{{/if}} myFT" aria-pressed="{{#if prioritised}}true{{else}}false{{/if}}" title="{{#if prioritised}}Unpin{{else}}Pin{{/if}} {{name}}">
17
- </button>
18
- </form>
19
- </div>
20
- {{/if}}
@@ -1,67 +0,0 @@
1
- {{#if @root.flags.myFtApiWrite}}
2
- <form class="n-myft-ui n-myft-ui--save" method="GET"
3
- data-content-id="{{contentId}}"
4
- data-myft-ui="saved"
5
- action="/myft/save/{{contentId}}"
6
- data-js-action="/__myft/api/core/saved/content/{{contentId}}?method=put">
7
- {{> n-myft-ui/components/csrf-token/input}}
8
- <div
9
- class="n-myft-ui__announcement o-normalise-visually-hidden"
10
- aria-live="assertive"
11
- data-pressed-text="Article saved in My FT."
12
- data-unpressed-text="Removed article from My FT."
13
- ></div>
14
- <button
15
- type="submit"
16
- class="{{#if saveButtonWithIcon}}n-myft-ui__save-button-with-icon{{else}}n-myft-ui__button{{#variant}} n-myft-ui__button--{{this}}{{/variant}}{{/if}}"
17
- data-trackable="{{#if trackableId}}{{trackableId}}{{else}}save-for-later{{/if}}"
18
- {{#if isSaved}}
19
- {{!-- The value of alternate label needs to be the opposite of label / the current saved state. This allows the client side JS to toggle the labels on state changes --}}
20
- title="{{#if title}}{{title}} is{{/if}} Saved to myFT"
21
- aria-label="{{#if title}}{{title}} is{{/if}} Saved to myFT"
22
- data-alternate-label="{{#if title}}Save {{title}} to myFT for later{{else}}Save this article to myFT for later{{/if}}"
23
- aria-pressed="true"
24
- {{else}}
25
- title="{{#if title}}Save {{title}} to myFT for later{{else}}Save this article to myFT for later{{/if}}"
26
- aria-label="{{#if title}}Save {{title}} to myFT for later{{else}}Save this article to myFT for later{{/if}}"
27
- data-alternate-label="{{#if title}}{{title}} is{{/if}} Saved to myFT"
28
- aria-pressed="false"
29
- {{/if}}
30
- {{#unlessEquals appIsStreamPage true}}
31
- {{#if saveButtonWithIcon}}
32
- data-text-variant="save-button-with-icon-copy"
33
- {{else}}
34
- data-text-variant="save-button-longer-copy"
35
- {{/if}}
36
- {{/unlessEquals}}
37
- {{#if alternateText}}
38
- data-alternate-text="{{alternateText}}"
39
- {{else}}
40
- {{#if isSaved}}
41
- data-alternate-text="Save&nbsp;"
42
- {{else}}
43
- data-alternate-text="Saved&nbsp;"
44
- {{/if}}
45
- {{/if}}
46
- data-content-id="{{contentId}}" {{! duplicated here for tracking}}
47
- >
48
- {{#if saveButtonWithIcon}}
49
- <span class="save-button-with-icon-copy" data-variant-label>{{#if buttonText~}}
50
- {{buttonText}}
51
- {{~else~}}
52
- {{#if isSaved}}Saved{{else}}Save{{/if}}
53
- {{~/if}}</span>
54
- {{else}}
55
- {{#if buttonText}}{{buttonText}}{{else}}
56
- {{#unlessEquals appIsStreamPage true}}
57
- <span class="save-button-longer-copy" data-variant-label>{{#if isSaved}}Saved&nbsp;{{else}}Save&nbsp;{{/if}}</span><span class="n-myft-ui__button--viewport-large" aria-hidden="true">to myFT</span>
58
- {{else}}
59
- <span>{{#if isSaved}}Saved{{else}}Save{{/if}}</span>
60
- {{/unlessEquals}}
61
- {{/if}}
62
- {{/if}}
63
- </button>
64
- </form>
65
- {{else}}
66
- <!-- Save button hidden due to myFtApiWrite being off -->
67
- {{/if }}
@@ -1,6 +0,0 @@
1
- {
2
- "conceptId": "00000000-0000-0000-0000-000000000044",
3
- "name": "Keith inc.",
4
- "directType": "http://www.ft.com/ontology/company/PublicCompany",
5
- "followPlusDigestEmail": true
6
- }
@@ -1,12 +0,0 @@
1
- <div class="o-grid-container o-grid-container--snappy">
2
- <h1>{{title}}</h1>
3
- <div class="o-grid-row">
4
- <div data-o-grid-colspan="12">
5
- <h2>Follow button</h2>
6
-
7
- {{#followButton}}
8
- {{> n-myft-ui/components/follow-button/follow-button}}
9
- {{/followButton}}
10
- </div>
11
- </div>
12
- </div>