@financial-times/n-myft-ui 26.1.0 → 27.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. package/.circleci/config.yml +46 -34
  2. package/.nvmrc +1 -1
  3. package/Makefile +0 -1
  4. package/README.md +2 -48
  5. package/build-state/npm-shrinkwrap.json +10147 -20187
  6. package/components/collections/collections.html +85 -0
  7. package/components/concept-list/concept-list.html +31 -0
  8. package/components/csrf-token/input.html +5 -0
  9. package/components/follow-button/follow-button.html +79 -0
  10. package/components/instant-alert/instant-alert.html +47 -0
  11. package/components/pin-button/pin-button.html +20 -0
  12. package/components/save-for-later/save-for-later.html +67 -0
  13. package/components/unread-articles-indicator/date-fns.js +6 -6
  14. package/demos/app.js +3 -26
  15. package/demos/templates/demo.html +11 -10
  16. package/package.json +16 -30
  17. package/components/collections/collections.jsx +0 -68
  18. package/components/collections/collections.test.js +0 -83
  19. package/components/concept-list/concept-list.jsx +0 -69
  20. package/components/concept-list/concept-list.test.js +0 -116
  21. package/components/csrf-token/input.jsx +0 -20
  22. package/components/csrf-token/input.test.js +0 -23
  23. package/components/follow-button/follow-button.jsx +0 -176
  24. package/components/follow-button/follow-button.test.js +0 -40
  25. package/components/index.js +0 -17
  26. package/components/instant-alert/instant-alert.jsx +0 -73
  27. package/components/instant-alert/instant-alert.test.js +0 -86
  28. package/components/pin-button/pin-button.jsx +0 -40
  29. package/components/pin-button/pin-button.test.js +0 -57
  30. package/components/save-for-later/save-for-later.jsx +0 -101
  31. package/components/save-for-later/save-for-later.test.js +0 -59
  32. package/demos/templates/demo-layout.html +0 -25
  33. package/demos/templates/demo.jsx +0 -125
  34. package/dist/bundles/bundle.js +0 -3232
  35. package/jest.config.js +0 -8
  36. package/jsx-migration.md +0 -16
  37. package/webpack.config.js +0 -34
@@ -1,68 +0,0 @@
1
- import React from 'react';
2
- import CsrfToken from '../csrf-token/input';
3
- import FollowButton from '../follow-button/follow-button';
4
-
5
- export default function Collections ({ title, liteStyle, flags, collectionName, trackable, concepts = [], csrfToken, cacheablePersonalisedUrl }) {
6
- const getLiteStyleModifier = () => liteStyle ? 'lite' : 'regular';
7
- let formProps = {
8
- method: 'POST',
9
- action: '#',
10
- 'data-myft-ui': 'follow',
11
- 'data-concept-id': concepts.map(concept => concept.id).join(',')
12
- };
13
-
14
- if (collectionName) {
15
- formProps['data-myft-tracking'] = collectionName;
16
- }
17
-
18
- return (
19
- <section
20
- className={`collection collection--${getLiteStyleModifier()}`}
21
- data-trackable={trackable ? trackable : 'collection'}>
22
- <header className={`collection__header collection__header--${getLiteStyleModifier()}`}>
23
- <h2 className={`collection__title collection__title--${getLiteStyleModifier()}`}>
24
- {title}
25
- </h2>
26
- </header>
27
- <ul className="collection__concepts">
28
- {concepts && concepts.map((concept, index) =>
29
- <li className="collection__concept" key={index}>
30
- <FollowButton cacheablePersonalisedUrl={cacheablePersonalisedUrl} csrfToken={csrfToken} variant={liteStyle ? 'primary' : 'inverse'} buttonText={concept.name} flags={flags} collectionName={collectionName} />
31
- </li>)
32
- }
33
- </ul>
34
-
35
- <div className="collection__meta">
36
- <form
37
- {...formProps}
38
- className="n-myft-ui n-myft-ui--follow n-ui-hide-core collection-follow-all">
39
- <input
40
- type="hidden"
41
- name="directType"
42
- value={concepts.map(concept => concept.directType).join(',')}
43
- />
44
- <CsrfToken csrfToken={csrfToken} cacheablePersonalisedUrl={cacheablePersonalisedUrl} />
45
- <input
46
- type="hidden"
47
- name="name"
48
- value={concepts.map(concept => concept.name).join(',')}
49
- />
50
- <button
51
- type="submit"
52
- aria-pressed="false"
53
- className={`collection-follow-all__button collection-follow-all__button--${getLiteStyleModifier()}`}
54
- data-trackable="follow all"
55
- data-concept-id={concepts.map(concept => concept.id).join(',')}
56
- aria-label={`Add all topics in the ${title} collection to my F T`}
57
- data-alternate-label={`Remove all topics in the ${title} collection from my F T`}
58
- data-alternate-text="Added"
59
- title={`Add all topics in the ${title} collection to my F T`}>
60
- Add all to myFT
61
- </button>
62
- </form>
63
- </div>
64
- </section>
65
-
66
- )
67
-
68
- }
@@ -1,83 +0,0 @@
1
- import React from 'react';
2
- import Collections from './collections';
3
- import { render, screen } from '@testing-library/react';
4
- import '@testing-library/jest-dom';
5
-
6
- const fixtures = {
7
- 'title': 'European Union',
8
- 'concepts': [
9
- {
10
- 'id': '00000000-0000-0000-0000-000000000000',
11
- 'prefLabel': 'EU immigration',
12
- 'directType': 'http://www.ft.com/ontology/Topic',
13
- 'url': 'https://www.ft.com/stream/00000000-0000-0000-0000-000000000000',
14
- 'name': 'EU immigration'
15
- },
16
- {
17
- 'id': '00000000-0000-0000-0000-000000000001',
18
- 'prefLabel': 'Europe Quantitative Easing',
19
- 'directType': 'http://www.ft.com/ontology/Topic',
20
- 'url': 'https://www.ft.com/stream/00000000-0000-0000-0000-000000000000',
21
- 'name': 'Europe Quantitative Easing'
22
- },
23
- {
24
- 'id': '00000000-0000-0000-0000-000000000002',
25
- 'prefLabel': 'EU financial regulation',
26
- 'directType': 'http://www.ft.com/ontology/Topic',
27
- 'url': 'https://www.ft.com/stream/00000000-0000-0000-0000-000000000000',
28
- 'name': 'EU financial regulation'
29
- },
30
- {
31
- 'id': '00000000-0000-0000-0000-000000000003',
32
- 'prefLabel': 'EU nothing',
33
- 'directType': 'http://www.ft.com/ontology/Topic',
34
- 'url': 'https://www.ft.com/stream/00000000-0000-0000-0000-000000000000',
35
- 'name': 'EU nothing'
36
- },
37
- {
38
- 'id': '00000000-0000-0000-0000-000000000004',
39
- 'prefLabel': 'EU trade',
40
- 'directType': 'http://www.ft.com/ontology/Topic',
41
- 'url': 'https://www.ft.com/stream/00000000-0000-0000-0000-000000000000',
42
- 'name': 'EU trade'
43
- }
44
- ]
45
- };
46
- const joinedDirectTypes = 'http://www.ft.com/ontology/Topic,http://www.ft.com/ontology/Topic,http://www.ft.com/ontology/Topic,http://www.ft.com/ontology/Topic,http://www.ft.com/ontology/Topic';
47
-
48
- const flags = {
49
- myFtApi: true,
50
- myFtApiWrite: true
51
- };
52
-
53
- describe('Concept List', () => {
54
-
55
- test('It renders the title of the collection', async () => {
56
- render(<Collections {...fixtures} flags={flags} />);
57
- expect(await screen.findByText('European Union')).toBeTruthy();
58
- });
59
-
60
- test('It renders label for the concept button', async () => {
61
- render(<Collections {...fixtures} flags={flags} />);
62
- expect(await screen.findByText('EU immigration')).toBeTruthy();
63
- expect(await screen.findByText('Europe Quantitative Easing')).toBeTruthy();
64
- expect(await screen.findByText('EU financial regulation')).toBeTruthy();
65
- expect(await screen.findByText('EU nothing')).toBeTruthy();
66
- expect(await screen.findByText('EU trade')).toBeTruthy();
67
- });
68
-
69
- test('It renders form "Add all to my FT" from', () => {
70
- const { container} = render(<Collections {...fixtures} flags={flags} />);
71
- const formElement = container.querySelector('form[action="#"]');
72
- expect(formElement).toBeTruthy();
73
- expect(formElement.method).toEqual('post');
74
- });
75
-
76
- test('It renders directType input with value of types joined', () => {
77
- const { container} = render(<Collections {...fixtures} flags={flags} />);
78
- const directTypeElement = container.querySelector('input[name="directType"]');
79
- expect(directTypeElement).toBeTruthy();
80
- expect(directTypeElement.value).toEqual(joinedDirectTypes);
81
- });
82
-
83
- });
@@ -1,69 +0,0 @@
1
- import React, { Fragment } from 'react';
2
- import FollowButton from '../follow-button/follow-button';
3
-
4
- export default function ConceptList ({ flags, concepts, contentType, conceptListTitle, trackable, csrfToken, cacheablePersonalisedUrl }) {
5
-
6
- const {
7
- myFtApi,
8
- myFtApiWrite
9
- } = flags;
10
-
11
- const generateTrackableProps = (primary, secondary) => {
12
- return {
13
- 'data-trackable': primary ? primary : secondary
14
- }
15
- }
16
-
17
- const shouldDisplay = () => {
18
- if(myFtApi && myFtApiWrite && Array.isArray(concepts) && concepts.length) {
19
- return true
20
- }
21
-
22
- return false;
23
- }
24
-
25
-
26
- return (
27
-
28
- <Fragment>
29
- {shouldDisplay() &&
30
- <div
31
- className='concept-list'
32
- {...generateTrackableProps(trackable, 'concept-list')}>
33
- {
34
- (contentType || conceptListTitle) &&
35
- <h2 className='concept-list__title'>
36
- {conceptListTitle ? conceptListTitle : `Follow the topics in this ${contentType}`}
37
- </h2>
38
- }
39
- <ul className='concept-list__list'>
40
- {concepts.map((concept, index) => {
41
- const {
42
- relativeUrl,
43
- url,
44
- conceptTrackable,
45
- prefLabel,
46
- id
47
- } = concept;
48
- return (
49
- <li key={index} className='concept-list__list-item'>
50
- {/* The relativeUrl and url point to the same resource. The url is the base path + the relative url.
51
- Example: browser_path = https://ft.com, relativeUrl = /capital-markets then url = https://www.ft.com/capital-markets.
52
-
53
- Note: we don't need to compute these urls in the business logic of these components as they're passed in as props.
54
-
55
- This note is just an explanation for why relativeUrl has preference over url.*/}
56
- <a
57
- href={relativeUrl || url}
58
- {...generateTrackableProps(conceptTrackable, 'concept')}
59
- className='concept-list__concept'>
60
- {prefLabel}
61
- </a>
62
- <FollowButton csrfToken={csrfToken} cacheablePersonalisedUrl={cacheablePersonalisedUrl} conceptId={id} name={prefLabel} flags={flags} />
63
- </li>
64
- )
65
- })}
66
- </ul>
67
- </div>}
68
- </Fragment>)
69
- }
@@ -1,116 +0,0 @@
1
- import React from 'react';
2
- import ConceptList from './concept-list';
3
- import { render, screen } from '@testing-library/react';
4
- import '@testing-library/jest-dom';
5
-
6
- const fixtures = [
7
- {
8
- 'conceptListTitle': 'Follow european union things',
9
- 'concepts': [
10
- {
11
- 'id': '00000000-0000-0000-0000-000000000161',
12
- 'prefLabel': 'EU immigration',
13
- 'directType': 'http://www.ft.com/ontology/Topic',
14
- 'url': 'https://www.ft.com/stream/00000000-0000-0000-0000-000000000161',
15
- 'name': 'EU immigration'
16
- },
17
- {
18
- 'id': '00000000-0000-0000-0000-000000000162',
19
- 'prefLabel': 'Europe Quantitative Easing',
20
- 'directType': 'http://www.ft.com/ontology/Topic',
21
- 'url': 'https://www.ft.com/stream/00000000-0000-0000-0000-000000000162',
22
- 'name': 'Europe Quantitative Easing'
23
- },
24
- {
25
- 'id': '00000000-0000-0000-0000-000000000163',
26
- 'prefLabel': 'EU financial regulation',
27
- 'directType': 'http://www.ft.com/ontology/Topic',
28
- 'url': 'https://www.ft.com/stream/00000000-0000-0000-0000-000000000163',
29
- 'name': 'EU financial regulation'
30
- },
31
- {
32
- 'id': '00000000-0000-0000-0000-000000000164',
33
- 'prefLabel': 'EU nothing',
34
- 'directType': 'http://www.ft.com/ontology/Topic',
35
- 'url': 'https://www.ft.com/stream/00000000-0000-0000-0000-000000000164',
36
- 'name': 'EU nothing'
37
- },
38
- {
39
- 'id': '00000000-0000-0000-0000-000000000165',
40
- 'prefLabel': 'EU trade',
41
- 'directType': 'http://www.ft.com/ontology/Topic',
42
- 'url': 'https://www.ft.com/stream/00000000-0000-0000-0000-000000000165',
43
- 'name': 'EU trade'
44
- }
45
- ]
46
- },
47
- {
48
- 'contentType': 'search',
49
- 'concepts': [
50
- {
51
- 'id': '00000000-0000-0000-0000-000000000166',
52
- 'prefLabel': 'Noodle',
53
- 'directType': 'http://www.ft.com/ontology/Topic',
54
- 'url': 'https://www.ft.com/stream/00000000-0000-0000-0000-000000000166',
55
- 'name': 'Noodle'
56
- },
57
- {
58
- 'id': '00000000-0000-0000-0000-000000000167',
59
- 'prefLabel': 'Green apples',
60
- 'directType': 'http://www.ft.com/ontology/Topic',
61
- 'url': 'https://www.ft.com/stream/00000000-0000-0000-0000-000000000167',
62
- 'name': 'Green apples'
63
- },
64
- {
65
- 'id': '00000000-0000-0000-0000-000000000168',
66
- 'prefLabel': 'Fox blood',
67
- 'directType': 'http://www.ft.com/ontology/Topic',
68
- 'url': 'https://www.ft.com/stream/00000000-0000-0000-0000-000000000168',
69
- 'name': 'Fox blood'
70
- },
71
- {
72
- 'id': '00000000-0000-0000-0000-000000000169',
73
- 'prefLabel': 'Dog party',
74
- 'directType': 'http://www.ft.com/ontology/Topic',
75
- 'url': 'https://www.ft.com/stream/00000000-0000-0000-0000-000000000169',
76
- 'name': 'Dog party'
77
- },
78
- {
79
- 'id': '00000000-0000-0000-0000-000000000170',
80
- 'prefLabel': 'Fifth thing',
81
- 'directType': 'http://www.ft.com/ontology/Topic',
82
- 'url': 'https://www.ft.com/stream/00000000-0000-0000-0000-000000000170',
83
- 'name': 'Fifth thing'
84
- }
85
- ]
86
- },
87
- ];
88
-
89
-
90
- const flags = {
91
- myFtApi: true,
92
- myFtApiWrite: true
93
- };
94
-
95
- describe('Concept List', () => {
96
-
97
- test('It renders conceptListTitle value as title when conceptListTitle is provided', async () => {
98
- render(<ConceptList {...fixtures[0]} flags={flags} />);
99
- expect(await screen.findByText('Follow european union things')).toBeTruthy();
100
- });
101
-
102
- test('It renders "Follow the topics in this {conceptType}" value as title when conceptType is provided', async () => {
103
- render(<ConceptList {...fixtures[1]} flags={flags} />);
104
- expect(await screen.findByText('Follow the topics in this search')).toBeTruthy();
105
- });
106
-
107
- test('It renders label for the concept button', async () => {
108
- render(<ConceptList {...fixtures[0]} flags={flags} />);
109
- expect(await screen.findByText('EU immigration')).toBeTruthy();
110
- expect(await screen.findByText('Europe Quantitative Easing')).toBeTruthy();
111
- expect(await screen.findByText('EU financial regulation')).toBeTruthy();
112
- expect(await screen.findByText('EU nothing')).toBeTruthy();
113
- expect(await screen.findByText('EU trade')).toBeTruthy();
114
- });
115
-
116
- });
@@ -1,20 +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.value = csrfToken;
9
- }
10
-
11
- return (
12
- <input
13
- data-myft-csrf-token
14
- {...inputProps}
15
- type="hidden"
16
- name="token"
17
- />
18
- );
19
-
20
- }
@@ -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]')).toBeTruthy();
20
- });
21
-
22
-
23
- });
@@ -1,176 +0,0 @@
1
- import React, {Fragment} 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
- csrfToken,
144
- cacheablePersonalisedUrl
145
- } = props;
146
-
147
- const formProps = generateFormProps(props);
148
- const buttonProps = generateButtonProps(props);
149
-
150
- const getVariantClass = (variant) => variant ? `n-myft-follow-button--${variant}` : '';
151
-
152
- return (
153
- <Fragment>
154
- {flags.myFtApiWrite && <form
155
- className={`n-myft-ui n-myft-ui--follow ${extraClasses || ''}`}
156
- method="GET"
157
- data-myft-ui="follow"
158
- data-concept-id={conceptId}
159
- {...formProps}>
160
- <CsrfToken cacheablePersonalisedUrl={cacheablePersonalisedUrl} csrfToken={csrfToken} />
161
- <div
162
- className="n-myft-ui__announcement o-normalise-visually-hidden"
163
- aria-live="assertive"
164
- data-pressed-text={`Now following ${name}.`}
165
- data-unpressed-text={`No longer following ${name}.`}
166
- ></div>
167
- <button
168
- {...buttonProps}
169
- className={[`n-myft-follow-button ${getVariantClass(variant)}`]}>
170
- {getButtonText(props)}
171
- </button>
172
- </form>}
173
- </Fragment>
174
- );
175
-
176
- }
@@ -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(await 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', async () => {
33
- render(<FollowButton {...props}
34
- variant={'standard'}
35
- setFollowButtonStateToSelected={true}
36
- cacheablePersonalisedUrl={true} />);
37
- expect(await screen.findByText('Added')).toBeTruthy();
38
- });
39
-
40
- });
@@ -1,17 +0,0 @@
1
- import CsrfToken from './csrf-token/input';
2
- import FollowButton from './follow-button/follow-button';
3
- import ConceptList from './concept-list/concept-list';
4
- import Collections from './collections/collections';
5
- import SaveForLater from './save-for-later/save-for-later';
6
- import PinButton from './pin-button/pin-button';
7
- import InstantAlert from './instant-alert/instant-alert';
8
-
9
- export {
10
- CsrfToken,
11
- FollowButton,
12
- ConceptList,
13
- Collections,
14
- SaveForLater,
15
- PinButton,
16
- InstantAlert
17
- };
@@ -1,73 +0,0 @@
1
- import React, { Fragment } from 'react';
2
- import CsrfToken from '../csrf-token/input';
3
-
4
- /**
5
- *
6
- * @param {Object} props
7
- * @param {string} props.name
8
- * @param {Object} props.flags
9
- * @param {booelan} props.hideButtonText
10
- * @param {string} props.conceptId
11
- * @param {string} props.name
12
- * @param {string} props.extraClasses
13
- * @param {boolean} props.directType
14
- * @param {string} props.cacheablePersonalisedUrl
15
- * @param {string} props.hasInstantAlert
16
- * @param {string} props.buttonText
17
- * @param {string} props.alternateText
18
- * @param {string} props.variant
19
- * @param {string} props.size
20
- */
21
- export default function InstantAlert (props) {
22
-
23
- const {
24
- hasInstantAlert,
25
- cacheablePersonalisedUrl,
26
- name,
27
- alternateText,
28
- buttonText,
29
- conceptId,
30
- variant,
31
- size,
32
- flags,
33
- hideButtonText,
34
- directType,
35
- extraClasses
36
- } = props;
37
-
38
- const generateButtonProps = () => {
39
-
40
- let buttonProps = {
41
- 'aria-pressed': `${Boolean(hasInstantAlert) && Boolean(cacheablePersonalisedUrl)}`,
42
- 'aria-label': `Get instant alerts for ${name}`,
43
- 'data-alternate-label': `Stop instant alerts for ${name}`,
44
- 'data-alternate-text': alternateText? alternateText: (buttonText ? buttonText : 'Instant alerts'),
45
- 'data-concept-id': conceptId, // duplicated here for tracking
46
- 'data-trackable': 'instant',
47
- title: `Get instant alerts for ${name}`,
48
- value: hasInstantAlert ? false : true,
49
- type: 'submit',
50
- name: '_rel.instant',
51
- className: `n-myft-ui__button n-myft-ui__button--instant n-myft-ui__button--instant-light${variant ? ` n-myft-ui__button--${variant}` : ''}${size ? ` n-myft-ui__button--${size}` : ''}`
52
- };
53
- return buttonProps;
54
- }
55
-
56
- return (
57
- <Fragment>
58
- {flags.myFtApiWrite &&
59
- <form className={`n-myft-ui n-myft-ui--instant${hideButtonText ? ' n-myft-ui--instant--hide-text' : ''}${extraClasses ? ` ${extraClasses}` : ''}`}
60
- method="GET"
61
- data-myft-ui="instant"
62
- data-concept-id={conceptId}
63
- action={`/myft/add/${conceptId}?instant=true`}
64
- data-js-action={`/__myft/api/core/followed/concept/${conceptId}?method=put`}>
65
- <CsrfToken />
66
- <input type="hidden" value={name} name="name" />
67
- <input type="hidden" value={directType || 'http://www.ft.com/ontology/concept/Concept'} name="directType" />
68
- <button {...generateButtonProps()}>{buttonText ? buttonText : 'Instant alerts'}</button>
69
- </form>}
70
- </Fragment>
71
- );
72
-
73
- }