@financial-times/n-myft-ui 25.0.1 → 27.1.0
Sign up to get free protection for your applications and to get access to all the features.
- package/.circleci/config.yml +42 -13
- package/.nvmrc +1 -1
- package/Makefile +0 -1
- package/README.md +4 -0
- package/build-state/npm-shrinkwrap.json +23540 -34370
- package/components/collections/collections.html +11 -3
- package/components/concept-list/concept-list.html +4 -1
- package/components/csrf-token/input.html +5 -0
- package/components/follow-button/follow-button.html +79 -0
- package/components/instant-alert/instant-alert.html +1 -1
- package/components/pin-button/pin-button.html +1 -1
- package/components/save-for-later/save-for-later.html +1 -1
- package/components/unread-articles-indicator/date-fns.js +6 -6
- package/demos/app.js +0 -25
- package/demos/templates/demo.html +2 -2
- package/package.json +15 -25
- package/components/csrf-token/__tests__/input.test.js +0 -23
- package/components/csrf-token/input.jsx +0 -26
- package/components/follow-button/__tests__/follow-button.test.js +0 -40
- package/components/follow-button/follow-button.jsx +0 -174
- package/demos/templates/demo-layout.html +0 -25
- package/demos/templates/demo.jsx +0 -33
- package/jest.config.js +0 -8
@@ -10,9 +10,17 @@
|
|
10
10
|
{{#concepts}}
|
11
11
|
<li class="collection__concept">
|
12
12
|
{{#if ../liteStyle}}
|
13
|
-
{{
|
13
|
+
{{> n-myft-ui/components/follow-button/follow-button
|
14
|
+
variant="primary"
|
15
|
+
buttonText=name
|
16
|
+
collectionName=../collectionName
|
17
|
+
}}
|
14
18
|
{{else}}
|
15
|
-
{{
|
19
|
+
{{> n-myft-ui/components/follow-button/follow-button
|
20
|
+
variant="inverse"
|
21
|
+
buttonText=name
|
22
|
+
collectionName=../collectionName
|
23
|
+
}}
|
16
24
|
{{/if}}
|
17
25
|
</li>
|
18
26
|
{{/concepts}}
|
@@ -42,7 +50,7 @@
|
|
42
50
|
{{~/unless~}}
|
43
51
|
{{~/concepts~}}"
|
44
52
|
/>
|
45
|
-
{{
|
53
|
+
{{> n-myft-ui/components/csrf-token/input}}
|
46
54
|
<input
|
47
55
|
type="hidden"
|
48
56
|
name="name"
|
@@ -20,7 +20,10 @@
|
|
20
20
|
class="concept-list__concept">
|
21
21
|
{{prefLabel}}
|
22
22
|
</a>
|
23
|
-
{{
|
23
|
+
{{> n-myft-ui/components/follow-button/follow-button
|
24
|
+
conceptId=id
|
25
|
+
name=prefLabel
|
26
|
+
}}
|
24
27
|
</li>
|
25
28
|
{{/each}}
|
26
29
|
</ul>
|
@@ -0,0 +1,79 @@
|
|
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}}
|
@@ -5,7 +5,7 @@
|
|
5
5
|
data-concept-id="{{conceptId}}"
|
6
6
|
action="/myft/add/{{conceptId}}?instant=true"
|
7
7
|
data-js-action="/__myft/api/core/followed/concept/{{conceptId}}?method=put">
|
8
|
-
{{
|
8
|
+
{{> n-myft-ui/components/csrf-token/input}}
|
9
9
|
<input type="hidden" value="{{name}}" name="name">
|
10
10
|
{{#if directType}}
|
11
11
|
<input type="hidden" value="{{directType}}" name="directType">
|
@@ -2,7 +2,7 @@
|
|
2
2
|
<span class="myft-pin-divider"></span>
|
3
3
|
<div class="myft-pin-button-wrapper">
|
4
4
|
<form method="post" action="/__myft/api/core/prioritised/concept/{{id}}?method={{#if prioritised}}delete{{else}}put{{/if}}" data-myft-prioritise>
|
5
|
-
{{
|
5
|
+
{{> n-myft-ui/components/csrf-token/input }}
|
6
6
|
<input type="hidden" value="{{name}}" name="name"> {{#if directType}}
|
7
7
|
<input type="hidden" value="{{directType}}" name="directType"> {{else}}
|
8
8
|
<input type="hidden" value="http://www.ft.com/ontology/concept/Concept" name="directType"> {{/if}}
|
@@ -4,7 +4,7 @@
|
|
4
4
|
data-myft-ui="saved"
|
5
5
|
action="/myft/save/{{contentId}}"
|
6
6
|
data-js-action="/__myft/api/core/saved/content/{{contentId}}?method=put">
|
7
|
-
{{
|
7
|
+
{{> n-myft-ui/components/csrf-token/input}}
|
8
8
|
<div
|
9
9
|
class="n-myft-ui__announcement o-normalise-visually-hidden"
|
10
10
|
aria-live="assertive"
|
@@ -2,12 +2,12 @@
|
|
2
2
|
// the detail => https://github.com/date-fns/date-fns/blob/HEAD/CHANGELOG.md#200---2019-08-20
|
3
3
|
// By adding validation for dates before their functions allows us to know it when unexpected value passed.
|
4
4
|
|
5
|
-
import isTodayOriginal from
|
6
|
-
import isAfterOriginal from
|
7
|
-
import addMinutesOriginal from
|
8
|
-
import startOfDayOriginal from
|
9
|
-
import isValidOriginal from
|
10
|
-
import parseISO from
|
5
|
+
import isTodayOriginal from "date-fns/src/isToday";
|
6
|
+
import isAfterOriginal from "date-fns/src/isAfter";
|
7
|
+
import addMinutesOriginal from "date-fns/src/addMinutes";
|
8
|
+
import startOfDayOriginal from "date-fns/src/startOfDay";
|
9
|
+
import isValidOriginal from "date-fns/src/isValid";
|
10
|
+
import parseISO from "date-fns/src/parseISO";
|
11
11
|
|
12
12
|
const isValid = (date) => {
|
13
13
|
if (!isValidOriginal(date)) {
|
package/demos/app.js
CHANGED
@@ -1,17 +1,11 @@
|
|
1
|
-
require('sucrase/register');
|
2
1
|
const nExpress = require('@financial-times/n-express');
|
3
2
|
const chalk = require('chalk');
|
4
3
|
const errorHighlight = chalk.bold.red;
|
5
4
|
const highlight = chalk.bold.green;
|
6
|
-
const { PageKitReactJSX } = require('@financial-times/dotcom-server-react-jsx');
|
7
|
-
const fs = require('fs');
|
8
5
|
const path = require('path');
|
9
6
|
const handlebars = require('handlebars');
|
10
7
|
const { PageKitHandlebars, helpers } = require('@financial-times/dotcom-server-handlebars');
|
11
8
|
|
12
|
-
const demoJSX = require('./templates/demo').default;
|
13
|
-
const demoLayoutSource = fs.readFileSync(path.join(__dirname, './templates/demo-layout.html'),'utf8').toString();
|
14
|
-
|
15
9
|
const fixtures = {
|
16
10
|
followButton: require('./fixtures/follow-button'),
|
17
11
|
saveButton: require('./fixtures/save-button'),
|
@@ -37,7 +31,6 @@ const app = module.exports = nExpress({
|
|
37
31
|
|
38
32
|
app.set('views', path.join(__dirname, '/templates'));
|
39
33
|
app.set('view engine', '.html');
|
40
|
-
|
41
34
|
app.engine('.html', new PageKitHandlebars({
|
42
35
|
cache: false,
|
43
36
|
handlebars,
|
@@ -48,8 +41,6 @@ app.engine('.html', new PageKitHandlebars({
|
|
48
41
|
|
49
42
|
app.use('/public', nExpress.static(path.join(__dirname, '../public'), { redirect: false }));
|
50
43
|
|
51
|
-
const jsxRenderer = (new PageKitReactJSX({ includeDoctype: false }));
|
52
|
-
|
53
44
|
app.get('/', (req, res) => {
|
54
45
|
res.render('demo', Object.assign({
|
55
46
|
title: 'n-myft-ui demo',
|
@@ -60,22 +51,6 @@ app.get('/', (req, res) => {
|
|
60
51
|
}, fixtures));
|
61
52
|
});
|
62
53
|
|
63
|
-
app.get('/demo-jsx', async (req, res) => {
|
64
|
-
let demo = await jsxRenderer.render(demoJSX, Object.assign({
|
65
|
-
title: 'n-myft-ui demo',
|
66
|
-
flags: {
|
67
|
-
myFtApi: true,
|
68
|
-
myFtApiWrite: true
|
69
|
-
}
|
70
|
-
}, fixtures));
|
71
|
-
|
72
|
-
let template = handlebars.compile(demoLayoutSource);
|
73
|
-
let result = template({body: demo});
|
74
|
-
|
75
|
-
res.send(result);
|
76
|
-
});
|
77
|
-
|
78
|
-
|
79
54
|
function runPa11yTests () {
|
80
55
|
const spawn = require('child_process').spawn;
|
81
56
|
const pa11y = spawn('pa11y-ci');
|
@@ -32,7 +32,7 @@
|
|
32
32
|
</h2>
|
33
33
|
|
34
34
|
{{#followButton}}
|
35
|
-
{{
|
35
|
+
{{> n-myft-ui/components/follow-button/follow-button}}
|
36
36
|
{{/followButton}}
|
37
37
|
|
38
38
|
<h2
|
@@ -41,7 +41,7 @@
|
|
41
41
|
</h2>
|
42
42
|
|
43
43
|
{{#followButton}}
|
44
|
-
{{
|
44
|
+
{{> n-myft-ui/components/follow-button/follow-button buttonText=name}}
|
45
45
|
{{/followButton}}
|
46
46
|
|
47
47
|
{{#saveButton}}
|
package/package.json
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
{
|
2
2
|
"name": "@financial-times/n-myft-ui",
|
3
|
-
"version": "
|
3
|
+
"version": "27.1.0",
|
4
4
|
"description": "Client side component for interaction with myft",
|
5
5
|
"main": "server.js",
|
6
6
|
"scripts": {
|
7
7
|
"test": "echo \"Error: no test specified\" && exit 1",
|
8
8
|
"commit": "commit-wizard",
|
9
9
|
"prepare": "npx snyk protect || npx snyk protect -d || true",
|
10
|
-
"preinstall": "
|
10
|
+
"preinstall": "npm_config_yes=true npx check-engine"
|
11
11
|
},
|
12
12
|
"repository": {
|
13
13
|
"type": "git",
|
@@ -20,18 +20,14 @@
|
|
20
20
|
},
|
21
21
|
"homepage": "https://github.com/Financial-Times/n-myft-ui#readme",
|
22
22
|
"devDependencies": {
|
23
|
-
"@financial-times/dotcom-build-bower-resolve": "
|
24
|
-
"@financial-times/dotcom-build-code-splitting": "0.
|
25
|
-
"@financial-times/dotcom-build-js": "0.
|
26
|
-
"@financial-times/dotcom-build-sass": "0.
|
27
|
-
"@financial-times/dotcom-page-kit-cli": "0.
|
28
|
-
"@financial-times/dotcom-server-handlebars": "^
|
29
|
-
"@financial-times/
|
30
|
-
"@financial-times/n-express": "^22.4.1",
|
23
|
+
"@financial-times/dotcom-build-bower-resolve": "^2.6.2",
|
24
|
+
"@financial-times/dotcom-build-code-splitting": "^5.0.0",
|
25
|
+
"@financial-times/dotcom-build-js": "^5.0.0",
|
26
|
+
"@financial-times/dotcom-build-sass": "^5.0.0",
|
27
|
+
"@financial-times/dotcom-page-kit-cli": "^0.6.5",
|
28
|
+
"@financial-times/dotcom-server-handlebars": "^5.0.0",
|
29
|
+
"@financial-times/n-express": "^23.0.1",
|
31
30
|
"@financial-times/n-gage": "^8.3.2",
|
32
|
-
"@sucrase/jest-plugin": "^2.2.0",
|
33
|
-
"@testing-library/jest-dom": "^5.16.1",
|
34
|
-
"@testing-library/react": "^12.1.2",
|
35
31
|
"ascii-table": "0.0.9",
|
36
32
|
"autoprefixer": "9.7.0",
|
37
33
|
"aws-sdk-mock": "4.5.0",
|
@@ -55,18 +51,14 @@
|
|
55
51
|
"css-loader": "^0.23.1",
|
56
52
|
"denodeify": "^1.2.1",
|
57
53
|
"eslint": "6.5.1",
|
58
|
-
"eslint-plugin-react": "^7.27.1",
|
59
54
|
"extract-css-block-webpack-plugin": "^1.3.0",
|
60
|
-
"extract-text-webpack-plugin": "3.0.2",
|
61
55
|
"fetch-mock": "^5.0.3",
|
62
56
|
"handlebars": "^4.0.6",
|
63
57
|
"handlebars-loader": "^1.4.0",
|
64
58
|
"http-server": "^0.11.1",
|
65
59
|
"hyperons": "^0.4.1",
|
66
60
|
"imports-loader": "0.8.0",
|
67
|
-
"inject-loader": "^
|
68
|
-
"jest": "^27.4.5",
|
69
|
-
"jsdom": "^19.0.0",
|
61
|
+
"inject-loader": "^4.0.1",
|
70
62
|
"karma": "4.4.1",
|
71
63
|
"karma-browserstack-launcher": "1.5.1",
|
72
64
|
"karma-chai": "^0.1.0",
|
@@ -77,31 +69,29 @@
|
|
77
69
|
"karma-sinon": "^1.0.5",
|
78
70
|
"karma-sinon-chai": "2.0.2",
|
79
71
|
"karma-sourcemap-loader": "^0.3.7",
|
80
|
-
"karma-webpack": "^
|
72
|
+
"karma-webpack": "^4.0.2",
|
81
73
|
"lintspaces-cli": "^0.7.0",
|
82
74
|
"lolex": "5.1.1",
|
83
75
|
"mocha": "6.2.2",
|
84
76
|
"mockery": "2.1.0",
|
85
77
|
"node-fetch": "2.6.0",
|
86
|
-
"node-sass": "^
|
78
|
+
"node-sass": "^6.0.1",
|
87
79
|
"nodemon": "^1.9.2",
|
88
80
|
"npm-prepublish": "^1.2.1",
|
89
81
|
"pa11y-ci": "^2.1.1",
|
90
82
|
"postcss-loader": "^0.9.1",
|
91
|
-
"react": "^17.0.2",
|
92
83
|
"regenerator-runtime": "^0.13.3",
|
93
84
|
"semver": "6.3.0",
|
94
85
|
"sinon": "^7.1.0",
|
95
86
|
"sinon-chai": "^3.2.0",
|
96
|
-
"snyk": "^1.216.5"
|
97
|
-
"sucrase": "^3.10.1"
|
87
|
+
"snyk": "^1.216.5"
|
98
88
|
},
|
99
89
|
"volta": {
|
100
|
-
"node": "
|
90
|
+
"node": "16.14.2",
|
101
91
|
"npm": "7.20.2"
|
102
92
|
},
|
103
93
|
"engines": {
|
104
|
-
"node": "
|
94
|
+
"node": "14.x || 16.x",
|
105
95
|
"npm": "7.x || 8.x"
|
106
96
|
},
|
107
97
|
"x-dash": {
|
@@ -1,23 +0,0 @@
|
|
1
|
-
import React from 'react';
|
2
|
-
import CsrfToken from '../input';
|
3
|
-
import { render } from '@testing-library/react';
|
4
|
-
import '@testing-library/jest-dom';
|
5
|
-
|
6
|
-
const props = {
|
7
|
-
cacheablePersonalisedUrl: false
|
8
|
-
};
|
9
|
-
|
10
|
-
describe('Csrf Token Input', () => {
|
11
|
-
|
12
|
-
test('It renders default button', async () => {
|
13
|
-
let { container } = render(<CsrfToken {...props} />);
|
14
|
-
expect(container.querySelector('[name=\'token\']')).toBeTruthy();
|
15
|
-
});
|
16
|
-
|
17
|
-
test('It renders csrf token attribute', async () => {
|
18
|
-
let { container } = render(<CsrfToken cacheablePersonalisedUrl={true} csrfToken={'test-token'} />);
|
19
|
-
expect(container.querySelector('[data-myft-csrf-token=\'test-token\']')).toBeTruthy();
|
20
|
-
});
|
21
|
-
|
22
|
-
|
23
|
-
});
|
@@ -1,26 +0,0 @@
|
|
1
|
-
import React from 'react';
|
2
|
-
|
3
|
-
export default function CsrfToken ({ cacheablePersonalisedUrl, csrfToken }) {
|
4
|
-
|
5
|
-
let inputProps = {};
|
6
|
-
|
7
|
-
if (cacheablePersonalisedUrl) {
|
8
|
-
inputProps = {
|
9
|
-
...inputProps,
|
10
|
-
'data-myft-csrf-token': csrfToken
|
11
|
-
};
|
12
|
-
}
|
13
|
-
|
14
|
-
if(csrfToken) {
|
15
|
-
inputProps.value = csrfToken;
|
16
|
-
}
|
17
|
-
|
18
|
-
return (
|
19
|
-
<input
|
20
|
-
{...inputProps}
|
21
|
-
type="hidden"
|
22
|
-
name="token"
|
23
|
-
/>
|
24
|
-
);
|
25
|
-
|
26
|
-
}
|
@@ -1,40 +0,0 @@
|
|
1
|
-
import React from 'react';
|
2
|
-
import FollowButton from '../follow-button';
|
3
|
-
import { render, screen } from '@testing-library/react';
|
4
|
-
import '@testing-library/jest-dom';
|
5
|
-
|
6
|
-
const props = {
|
7
|
-
flags: {
|
8
|
-
myFtApi: true,
|
9
|
-
myFtApiWrite: true
|
10
|
-
},
|
11
|
-
conceptId: '0000-000000-00000-0000',
|
12
|
-
name: 'Follow button'
|
13
|
-
};
|
14
|
-
|
15
|
-
describe('Follow button', () => {
|
16
|
-
|
17
|
-
test('It renders default button', async () => {
|
18
|
-
render(<FollowButton {...props} />);
|
19
|
-
expect(screen.findByText('Add to myFT')).toBeTruthy();
|
20
|
-
});
|
21
|
-
|
22
|
-
test('It renders a variant', async () => {
|
23
|
-
const { container } = render(<FollowButton {...props} variant={'standard'} />);
|
24
|
-
expect(container.getElementsByClassName('n-myft-follow-button--standard')).toHaveLength(1);
|
25
|
-
});
|
26
|
-
|
27
|
-
test('It renders follow button form', async () => {
|
28
|
-
const { container } = render(<FollowButton {...props} variant={'standard'} />);
|
29
|
-
expect(container.querySelector(`form[action='/myft/add/${props.conceptId}']`)).toBeTruthy();
|
30
|
-
});
|
31
|
-
|
32
|
-
test('Button state changes when attributes change', () => {
|
33
|
-
render(<FollowButton {...props}
|
34
|
-
variant={'standard'}
|
35
|
-
setFollowButtonStateToSelected={true}
|
36
|
-
cacheablePersonalisedUrl={true} />);
|
37
|
-
expect(screen.findByText('Added')).toBeTruthy();
|
38
|
-
});
|
39
|
-
|
40
|
-
});
|
@@ -1,174 +0,0 @@
|
|
1
|
-
import React from 'react';
|
2
|
-
import CsrfToken from '../csrf-token/input';
|
3
|
-
|
4
|
-
function generateFormProps (props) {
|
5
|
-
let generatedProps = {};
|
6
|
-
|
7
|
-
const {
|
8
|
-
collectionName,
|
9
|
-
followPlusDigestEmail,
|
10
|
-
conceptId,
|
11
|
-
setFollowButtonStateToSelected,
|
12
|
-
cacheablePersonalisedUrl
|
13
|
-
} = props;
|
14
|
-
|
15
|
-
if (collectionName) {
|
16
|
-
generatedProps['data-myft-tracking'] = `collectionName=${collectionName}`;
|
17
|
-
}
|
18
|
-
|
19
|
-
if(followPlusDigestEmail) {
|
20
|
-
generatedProps['action'] = `/__myft/api/core/follow-plus-digest-email/${conceptId}?method=put`;
|
21
|
-
generatedProps['data-myft-ui-variant'] = 'followPlusDigestEmail';
|
22
|
-
} else {
|
23
|
-
if(setFollowButtonStateToSelected && cacheablePersonalisedUrl) {
|
24
|
-
generatedProps['action'] = `/myft/remove/${conceptId}`;
|
25
|
-
generatedProps['data-js-action'] = `/__myft/api/core/followed/concept/${conceptId}?method=delete`;
|
26
|
-
} else {
|
27
|
-
generatedProps['action'] = `/myft/add/${conceptId}`;
|
28
|
-
generatedProps['data-js-action'] = `/__myft/api/core/followed/concept/${conceptId}?method=put`;
|
29
|
-
}
|
30
|
-
}
|
31
|
-
|
32
|
-
return generatedProps;
|
33
|
-
|
34
|
-
}
|
35
|
-
|
36
|
-
function generateButtonProps (props) {
|
37
|
-
|
38
|
-
const {
|
39
|
-
cacheablePersonalisedUrl,
|
40
|
-
setFollowButtonStateToSelected,
|
41
|
-
name,
|
42
|
-
buttonText,
|
43
|
-
variant,
|
44
|
-
conceptId,
|
45
|
-
alternateText,
|
46
|
-
followPlusDigestEmail
|
47
|
-
} = props;
|
48
|
-
|
49
|
-
let generatedProps = {
|
50
|
-
'data-concept-id': conceptId,
|
51
|
-
'n-myft-follow-button': 'true',
|
52
|
-
'data-trackable': 'follow',
|
53
|
-
type: 'submit'
|
54
|
-
};
|
55
|
-
|
56
|
-
if (cacheablePersonalisedUrl && setFollowButtonStateToSelected) {
|
57
|
-
generatedProps['aria-label'] = `Remove ${name} from myFT`;
|
58
|
-
generatedProps['title'] = `Remove ${name} from myFT`
|
59
|
-
generatedProps['data-alternate-label'] = `Add ${name} to myFT`;
|
60
|
-
generatedProps['aria-pressed'] = true;
|
61
|
-
|
62
|
-
if(alternateText) {
|
63
|
-
generatedProps['data-alternate-text'] = alternateText;
|
64
|
-
} else {
|
65
|
-
if(buttonText) {
|
66
|
-
generatedProps['data-alternate-text'] = buttonText;
|
67
|
-
} else {
|
68
|
-
generatedProps['data-alternate-text'] = 'Add to myFT';
|
69
|
-
}
|
70
|
-
}
|
71
|
-
} else {
|
72
|
-
generatedProps['aria-pressed'] = false;
|
73
|
-
generatedProps['aria-label'] = `Add ${name} to myFT`;
|
74
|
-
generatedProps['title'] = `Add ${name} to myFT`;
|
75
|
-
generatedProps['data-alternate-label'] = `Remove ${name} from myFT`;
|
76
|
-
if (alternateText) {
|
77
|
-
generatedProps['data-alternate-text'] = alternateText;
|
78
|
-
} else {
|
79
|
-
if (buttonText) {
|
80
|
-
generatedProps['data-alternate-text'] = buttonText;
|
81
|
-
} else {
|
82
|
-
generatedProps['data-alternate-text'] = 'Added';
|
83
|
-
}
|
84
|
-
}
|
85
|
-
}
|
86
|
-
|
87
|
-
if(variant) {
|
88
|
-
generatedProps[`n-myft-follow-button--${variant}`] = 'true';
|
89
|
-
}
|
90
|
-
|
91
|
-
if(followPlusDigestEmail) {
|
92
|
-
generatedProps['data-trackable-context-messaging'] = 'add-to-myft-plus-digest-button';
|
93
|
-
}
|
94
|
-
|
95
|
-
return generatedProps;
|
96
|
-
}
|
97
|
-
|
98
|
-
function getButtonText (props) {
|
99
|
-
|
100
|
-
const {
|
101
|
-
buttonText,
|
102
|
-
setFollowButtonStateToSelected,
|
103
|
-
cacheablePersonalisedUrl
|
104
|
-
} = props;
|
105
|
-
let outputText;
|
106
|
-
|
107
|
-
if(buttonText) {
|
108
|
-
outputText = buttonText;
|
109
|
-
} else {
|
110
|
-
if(setFollowButtonStateToSelected && cacheablePersonalisedUrl) {
|
111
|
-
outputText = 'Added';
|
112
|
-
} else {
|
113
|
-
outputText = 'Add to myFT';
|
114
|
-
}
|
115
|
-
}
|
116
|
-
|
117
|
-
return outputText;
|
118
|
-
}
|
119
|
-
|
120
|
-
/**
|
121
|
-
*
|
122
|
-
* @param {Object} props
|
123
|
-
* @param {string} props.name
|
124
|
-
* @param {Object} props.flags
|
125
|
-
* @param {string} props.extraClasses
|
126
|
-
* @param {string} props.conceptId
|
127
|
-
* @param {string} props.variant
|
128
|
-
* @param {string} props.buttonText
|
129
|
-
* @param {*} props.setFollowButtonStateToSelected
|
130
|
-
* @param {string} props.cacheablePersonalisedUrl
|
131
|
-
* @param {string} props.alternateText
|
132
|
-
* @param {*} props.followPlusDigestEmail
|
133
|
-
* @param {string} props.collectionName
|
134
|
-
*/
|
135
|
-
export default function FollowButton (props) {
|
136
|
-
|
137
|
-
const {
|
138
|
-
name,
|
139
|
-
flags,
|
140
|
-
extraClasses,
|
141
|
-
conceptId,
|
142
|
-
variant,
|
143
|
-
} = props;
|
144
|
-
|
145
|
-
const formProps = generateFormProps(props);
|
146
|
-
const buttonProps = generateButtonProps(props);
|
147
|
-
|
148
|
-
const getVariantClass = (variant) => variant ? `n-myft-follow-button--${variant}` : '';
|
149
|
-
|
150
|
-
return (
|
151
|
-
<>
|
152
|
-
{flags.myFtApiWrite && <form
|
153
|
-
className={`n-myft-ui n-myft-ui--follow ${extraClasses || ''}`}
|
154
|
-
method="GET"
|
155
|
-
data-myft-ui="follow"
|
156
|
-
data-concept-id={conceptId}
|
157
|
-
{...formProps}>
|
158
|
-
<CsrfToken cacheablePersonalisedUrl={props.cacheablePersonalisedUrl} csrfToken={props.csrfToken} />
|
159
|
-
<div
|
160
|
-
className="n-myft-ui__announcement o-normalise-visually-hidden"
|
161
|
-
aria-live="assertive"
|
162
|
-
data-pressed-text={`Now following ${name}.`}
|
163
|
-
data-unpressed-text={`No longer following ${name}.`}
|
164
|
-
></div>
|
165
|
-
<button
|
166
|
-
{...buttonProps}
|
167
|
-
className={[`n-myft-follow-button ${getVariantClass(variant)}`]}>
|
168
|
-
{getButtonText(props)}
|
169
|
-
</button>
|
170
|
-
</form>}
|
171
|
-
</>
|
172
|
-
);
|
173
|
-
|
174
|
-
}
|
@@ -1,25 +0,0 @@
|
|
1
|
-
<!DOCTYPE html>
|
2
|
-
<html lang="en">
|
3
|
-
|
4
|
-
<head>
|
5
|
-
<meta charset="utf-8">
|
6
|
-
<title>{{title}}</title>
|
7
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
8
|
-
<link rel="stylesheet" href="/public/main.css">
|
9
|
-
</head>
|
10
|
-
|
11
|
-
<body>
|
12
|
-
|
13
|
-
<div class="o-grid-container o-grid-container--snappy">
|
14
|
-
<div class="o-grid-row">
|
15
|
-
<ul>
|
16
|
-
<li><a href="/">Basic</a></li>
|
17
|
-
<li><a href="/demo-jsx">JSX demo</a></li>
|
18
|
-
</ul>
|
19
|
-
</div>
|
20
|
-
</div>
|
21
|
-
|
22
|
-
{{{body}}}
|
23
|
-
</body>
|
24
|
-
|
25
|
-
</html>
|