@financial-times/n-myft-ui 28.2.0 → 28.2.1

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.
@@ -65,6 +65,7 @@
65
65
  "karma-sinon": "^1.0.5",
66
66
  "karma-sinon-chai": "2.0.2",
67
67
  "karma-sourcemap-loader": "^0.3.7",
68
+ "karma-viewport": "^1.0.9",
68
69
  "karma-webpack": "^4.0.2",
69
70
  "lintspaces-cli": "^0.7.0",
70
71
  "lolex": "5.1.1",
@@ -3383,6 +3384,84 @@
3383
3384
  "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==",
3384
3385
  "dev": true
3385
3386
  },
3387
+ "node_modules/@types/karma": {
3388
+ "version": "6.3.3",
3389
+ "resolved": "https://registry.npmjs.org/@types/karma/-/karma-6.3.3.tgz",
3390
+ "integrity": "sha512-nRMec4mTCt+tkpRqh5/pAxmnjzEgAaalIq7mdfLFH88gSRC8+bxejLiSjHMMT/vHIhJHqg4GPIGCnCFbwvDRww==",
3391
+ "dev": true,
3392
+ "dependencies": {
3393
+ "@types/node": "*",
3394
+ "log4js": "^6.4.1"
3395
+ }
3396
+ },
3397
+ "node_modules/@types/karma/node_modules/date-format": {
3398
+ "version": "4.0.13",
3399
+ "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.13.tgz",
3400
+ "integrity": "sha512-bnYCwf8Emc3pTD8pXnre+wfnjGtfi5ncMDKy7+cWZXbmRAsdWkOQHrfC1yz/KiwP5thDp2kCHWYWKBX4HP1hoQ==",
3401
+ "dev": true,
3402
+ "engines": {
3403
+ "node": ">=4.0"
3404
+ }
3405
+ },
3406
+ "node_modules/@types/karma/node_modules/flatted": {
3407
+ "version": "3.2.6",
3408
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.6.tgz",
3409
+ "integrity": "sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ==",
3410
+ "dev": true
3411
+ },
3412
+ "node_modules/@types/karma/node_modules/fs-extra": {
3413
+ "version": "8.1.0",
3414
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
3415
+ "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
3416
+ "dev": true,
3417
+ "dependencies": {
3418
+ "graceful-fs": "^4.2.0",
3419
+ "jsonfile": "^4.0.0",
3420
+ "universalify": "^0.1.0"
3421
+ },
3422
+ "engines": {
3423
+ "node": ">=6 <7 || >=8"
3424
+ }
3425
+ },
3426
+ "node_modules/@types/karma/node_modules/jsonfile": {
3427
+ "version": "4.0.0",
3428
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
3429
+ "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==",
3430
+ "dev": true,
3431
+ "optionalDependencies": {
3432
+ "graceful-fs": "^4.1.6"
3433
+ }
3434
+ },
3435
+ "node_modules/@types/karma/node_modules/log4js": {
3436
+ "version": "6.6.1",
3437
+ "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.6.1.tgz",
3438
+ "integrity": "sha512-J8VYFH2UQq/xucdNu71io4Fo+purYYudyErgBbswWKO0MC6QVOERRomt5su/z6d3RJSmLyTGmXl3Q/XjKCf+/A==",
3439
+ "dev": true,
3440
+ "dependencies": {
3441
+ "date-format": "^4.0.13",
3442
+ "debug": "^4.3.4",
3443
+ "flatted": "^3.2.6",
3444
+ "rfdc": "^1.3.0",
3445
+ "streamroller": "^3.1.2"
3446
+ },
3447
+ "engines": {
3448
+ "node": ">=8.0"
3449
+ }
3450
+ },
3451
+ "node_modules/@types/karma/node_modules/streamroller": {
3452
+ "version": "3.1.2",
3453
+ "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.1.2.tgz",
3454
+ "integrity": "sha512-wZswqzbgGGsXYIrBYhOE0yP+nQ6XRk7xDcYwuQAGTYXdyAUmvgVFE0YU1g5pvQT0m7GBaQfYcSnlHbapuK0H0A==",
3455
+ "dev": true,
3456
+ "dependencies": {
3457
+ "date-format": "^4.0.13",
3458
+ "debug": "^4.3.4",
3459
+ "fs-extra": "^8.1.0"
3460
+ },
3461
+ "engines": {
3462
+ "node": ">=8.0"
3463
+ }
3464
+ },
3386
3465
  "node_modules/@types/mdast": {
3387
3466
  "version": "3.0.10",
3388
3467
  "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.10.tgz",
@@ -12267,6 +12346,15 @@
12267
12346
  "graceful-fs": "^4.1.6"
12268
12347
  }
12269
12348
  },
12349
+ "node_modules/jsonschema": {
12350
+ "version": "1.4.1",
12351
+ "resolved": "https://registry.npmjs.org/jsonschema/-/jsonschema-1.4.1.tgz",
12352
+ "integrity": "sha512-S6cATIPVv1z0IlxdN+zUk5EPjkGCdnhN4wVSBlvoUO1tOLJootbo9CquNJmbIh4yikWHiUedhRYrNPn1arpEmQ==",
12353
+ "dev": true,
12354
+ "engines": {
12355
+ "node": "*"
12356
+ }
12357
+ },
12270
12358
  "node_modules/jsprim": {
12271
12359
  "version": "1.4.2",
12272
12360
  "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz",
@@ -12441,6 +12529,16 @@
12441
12529
  "graceful-fs": "^4.1.2"
12442
12530
  }
12443
12531
  },
12532
+ "node_modules/karma-viewport": {
12533
+ "version": "1.0.9",
12534
+ "resolved": "https://registry.npmjs.org/karma-viewport/-/karma-viewport-1.0.9.tgz",
12535
+ "integrity": "sha512-E1xVe66vBQtI66TGOtZMzV5nf6BW5tW4TQVUqPK+oakVLdsG/ZUG688tGK0lL1q0t7nfQD1dwLD8Z9Guu/RVdg==",
12536
+ "dev": true,
12537
+ "dependencies": {
12538
+ "@types/karma": "^6.3.3",
12539
+ "jsonschema": "^1.4.0"
12540
+ }
12541
+ },
12444
12542
  "node_modules/karma-webpack": {
12445
12543
  "version": "4.0.2",
12446
12544
  "resolved": "https://registry.npmjs.org/karma-webpack/-/karma-webpack-4.0.2.tgz",
@@ -25822,6 +25920,74 @@
25822
25920
  "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==",
25823
25921
  "dev": true
25824
25922
  },
25923
+ "@types/karma": {
25924
+ "version": "6.3.3",
25925
+ "resolved": "https://registry.npmjs.org/@types/karma/-/karma-6.3.3.tgz",
25926
+ "integrity": "sha512-nRMec4mTCt+tkpRqh5/pAxmnjzEgAaalIq7mdfLFH88gSRC8+bxejLiSjHMMT/vHIhJHqg4GPIGCnCFbwvDRww==",
25927
+ "dev": true,
25928
+ "requires": {
25929
+ "@types/node": "*",
25930
+ "log4js": "^6.4.1"
25931
+ },
25932
+ "dependencies": {
25933
+ "date-format": {
25934
+ "version": "4.0.13",
25935
+ "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.13.tgz",
25936
+ "integrity": "sha512-bnYCwf8Emc3pTD8pXnre+wfnjGtfi5ncMDKy7+cWZXbmRAsdWkOQHrfC1yz/KiwP5thDp2kCHWYWKBX4HP1hoQ==",
25937
+ "dev": true
25938
+ },
25939
+ "flatted": {
25940
+ "version": "3.2.6",
25941
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.6.tgz",
25942
+ "integrity": "sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ==",
25943
+ "dev": true
25944
+ },
25945
+ "fs-extra": {
25946
+ "version": "8.1.0",
25947
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
25948
+ "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
25949
+ "dev": true,
25950
+ "requires": {
25951
+ "graceful-fs": "^4.2.0",
25952
+ "jsonfile": "^4.0.0",
25953
+ "universalify": "^0.1.0"
25954
+ }
25955
+ },
25956
+ "jsonfile": {
25957
+ "version": "4.0.0",
25958
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
25959
+ "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==",
25960
+ "dev": true,
25961
+ "requires": {
25962
+ "graceful-fs": "^4.1.6"
25963
+ }
25964
+ },
25965
+ "log4js": {
25966
+ "version": "6.6.1",
25967
+ "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.6.1.tgz",
25968
+ "integrity": "sha512-J8VYFH2UQq/xucdNu71io4Fo+purYYudyErgBbswWKO0MC6QVOERRomt5su/z6d3RJSmLyTGmXl3Q/XjKCf+/A==",
25969
+ "dev": true,
25970
+ "requires": {
25971
+ "date-format": "^4.0.13",
25972
+ "debug": "^4.3.4",
25973
+ "flatted": "^3.2.6",
25974
+ "rfdc": "^1.3.0",
25975
+ "streamroller": "^3.1.2"
25976
+ }
25977
+ },
25978
+ "streamroller": {
25979
+ "version": "3.1.2",
25980
+ "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.1.2.tgz",
25981
+ "integrity": "sha512-wZswqzbgGGsXYIrBYhOE0yP+nQ6XRk7xDcYwuQAGTYXdyAUmvgVFE0YU1g5pvQT0m7GBaQfYcSnlHbapuK0H0A==",
25982
+ "dev": true,
25983
+ "requires": {
25984
+ "date-format": "^4.0.13",
25985
+ "debug": "^4.3.4",
25986
+ "fs-extra": "^8.1.0"
25987
+ }
25988
+ }
25989
+ }
25990
+ },
25825
25991
  "@types/mdast": {
25826
25992
  "version": "3.0.10",
25827
25993
  "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.10.tgz",
@@ -33156,6 +33322,12 @@
33156
33322
  "universalify": "^0.1.2"
33157
33323
  }
33158
33324
  },
33325
+ "jsonschema": {
33326
+ "version": "1.4.1",
33327
+ "resolved": "https://registry.npmjs.org/jsonschema/-/jsonschema-1.4.1.tgz",
33328
+ "integrity": "sha512-S6cATIPVv1z0IlxdN+zUk5EPjkGCdnhN4wVSBlvoUO1tOLJootbo9CquNJmbIh4yikWHiUedhRYrNPn1arpEmQ==",
33329
+ "dev": true
33330
+ },
33159
33331
  "jsprim": {
33160
33332
  "version": "1.4.2",
33161
33333
  "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz",
@@ -33415,6 +33587,16 @@
33415
33587
  "graceful-fs": "^4.1.2"
33416
33588
  }
33417
33589
  },
33590
+ "karma-viewport": {
33591
+ "version": "1.0.9",
33592
+ "resolved": "https://registry.npmjs.org/karma-viewport/-/karma-viewport-1.0.9.tgz",
33593
+ "integrity": "sha512-E1xVe66vBQtI66TGOtZMzV5nf6BW5tW4TQVUqPK+oakVLdsG/ZUG688tGK0lL1q0t7nfQD1dwLD8Z9Guu/RVdg==",
33594
+ "dev": true,
33595
+ "requires": {
33596
+ "@types/karma": "^6.3.3",
33597
+ "jsonschema": "^1.4.0"
33598
+ }
33599
+ },
33418
33600
  "karma-webpack": {
33419
33601
  "version": "4.0.2",
33420
33602
  "resolved": "https://registry.npmjs.org/karma-webpack/-/karma-webpack-4.0.2.tgz",
package/karma.conf.js CHANGED
@@ -10,7 +10,7 @@ module.exports = function (karma) {
10
10
 
11
11
  // frameworks to use
12
12
  // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
13
- frameworks: ['mocha', 'chai', 'sinon', 'sinon-chai'],
13
+ frameworks: ['mocha', 'chai', 'sinon', 'sinon-chai', 'viewport'],
14
14
 
15
15
  // list of files / patterns to load in the browser
16
16
  files: [
@@ -110,7 +110,8 @@ module.exports = function (karma) {
110
110
  require('karma-webpack'),
111
111
  require('karma-chrome-launcher'),
112
112
  require('karma-browserstack-launcher'),
113
- require('karma-html-reporter')
113
+ require('karma-html-reporter'),
114
+ require('karma-viewport')
114
115
  ],
115
116
  client: {
116
117
  mocha: {
package/myft/main.scss CHANGED
@@ -323,4 +323,34 @@ $spacing-unit: 20px;
323
323
  }
324
324
  }
325
325
  }
326
+
327
+ .myft-notification {
328
+ background: oColorsByName('white-80');
329
+ box-sizing: border-box;
330
+ display: flex;
331
+ align-items: center;
332
+ justify-content: center;
333
+ position: absolute;
334
+ border-radius: 5px;
335
+ font-family: MetricWeb, sans-serif;
336
+ font-size: 18px;
337
+ }
338
+
339
+ .share-nav__vertical {
340
+ .myft-notification {
341
+ top: 175px;
342
+ width: 340px;
343
+ height: 44px;
344
+ left: 50px;
345
+ }
346
+ }
347
+
348
+ .share-nav__horizontal {
349
+ .myft-notification {
350
+ top: -52px;
351
+ width: 340px;
352
+ height: 44px;
353
+ z-index: 10;
354
+ }
355
+ }
326
356
  }
@@ -0,0 +1,5 @@
1
+ export default function stringToHTMLElement (string) {
2
+ const template = document.createElement('template');
3
+ template.innerHTML = string.trim();
4
+ return template.content.firstChild;
5
+ }
@@ -0,0 +1,7 @@
1
+ const MOBILE_BREAKPOINT = 980;
2
+
3
+ export default function isMobile () {
4
+ const vw = Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0);
5
+
6
+ return vw <= MOBILE_BREAKPOINT;
7
+ }
package/myft/ui/lists.js CHANGED
@@ -5,8 +5,10 @@ import * as myFtUiButtonStates from './lib/button-states';
5
5
  import nNotification from '@financial-times/n-notification';
6
6
  import { uuid } from 'n-ui-foundations';
7
7
  import getToken from './lib/get-csrf-token';
8
+ import isMobile from './lib/is-mobile';
8
9
  import oForms from '@financial-times/o-forms';
9
10
  import openSaveArticleToListVariant from './save-article-to-list-variant';
11
+ import stringToHTMLElement from './lib/convert-string-to-html-element';
10
12
 
11
13
  const delegate = new Delegate(document.body);
12
14
  const csrfToken = getToken();
@@ -265,14 +267,23 @@ function initialEventListeners () {
265
267
  // Checks if the createListAndSaveArticle variant is active
266
268
  // and will show the variant overlay if the user has no lists,
267
269
  // otherwise it will show the classic overlay
268
- const createNewListDesign = event.currentTarget.querySelector('[data-myft-ui-save-new="manageArticleLists"]');
269
- if (createNewListDesign) {
270
+ const newListDesign = event.currentTarget.querySelector('[data-myft-ui-save-new="manageArticleLists"]');
271
+ if (newListDesign) {
270
272
  return openCreateListAndAddArticleOverlay(contentId);
271
273
  }
272
274
 
273
275
  handleArticleSaved(contentId);
274
276
  });
275
277
 
278
+ document.body.addEventListener('myft.user.saved.content.remove', event => {
279
+ const contentId = event.detail.subject;
280
+
281
+ const newListDesign = event.currentTarget.querySelector('[data-myft-ui-save-new="manageArticleLists"]');
282
+ if (newListDesign) {
283
+ return showUnsavedNotification(contentId);
284
+ }
285
+ });
286
+
276
287
  delegate.on('click', '[data-myft-ui="copy-to-list"]', event => {
277
288
  event.preventDefault();
278
289
  showCopyToListOverlay(event.target.getAttribute('data-content-id'), event.target.getAttribute('data-actor-id'));
@@ -285,6 +296,34 @@ function initialEventListeners () {
285
296
  delegate.on('submit', '[data-myft-ui="contained"]', handleRemoveToggleSubmit);
286
297
  }
287
298
 
299
+ function showUnsavedNotification () {
300
+ const parentSelector = isMobile() ? '.o-share--horizontal' : '.o-share--vertical';
301
+ const parentNode = document.querySelector(parentSelector);
302
+
303
+ // We're not supporting multiple notifications for now
304
+ // If a notification is present, we'll silently avoid showing another
305
+ if (document.querySelector('.myft-notification') || !parentNode) {
306
+ return;
307
+ }
308
+
309
+ const content = `
310
+ <p role="alert">Removed from <a href="https://www.ft.com/myft/saved-articles">saved articles</a> in myFT</p>
311
+ `;
312
+
313
+ const contentNode = stringToHTMLElement(content);
314
+
315
+ const container = document.createElement('div');
316
+ container.className = 'myft-notification';
317
+ container.appendChild(contentNode);
318
+
319
+ parentNode.appendChild(container);
320
+
321
+ setTimeout(
322
+ () => parentNode.removeChild(container),
323
+ 5 * 1000
324
+ );
325
+ }
326
+
288
327
  export function init () {
289
328
  initialEventListeners();
290
329
  }
@@ -2,6 +2,8 @@ import Overlay from '@financial-times/o-overlay';
2
2
  import myFtClient from 'next-myft-client';
3
3
  import { uuid } from 'n-ui-foundations';
4
4
  import getToken from './lib/get-csrf-token';
5
+ import isMobile from './lib/is-mobile';
6
+ import stringToHTMLElement from './lib/convert-string-to-html-element';
5
7
 
6
8
  const csrfToken = getToken();
7
9
 
@@ -137,12 +139,6 @@ export default async function openSaveArticleToListVariant (name, contentId) {
137
139
  });
138
140
  }
139
141
 
140
- function stringToHTMLElement (string) {
141
- const template = document.createElement('template');
142
- template.innerHTML = string.trim();
143
- return template.content.firstChild;
144
- }
145
-
146
142
  function FormElement (createList) {
147
143
  const formString = `
148
144
  <form class="myft-ui-create-list-variant-form">
@@ -311,12 +307,6 @@ function positionOverlay (target) {
311
307
  }
312
308
  }
313
309
 
314
- function isMobile () {
315
- const vw = Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0);
316
-
317
- return vw <= 980;
318
- }
319
-
320
310
  function calculateLargerScreenHalf (target) {
321
311
  if (!target) {
322
312
  return 'BELOW';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@financial-times/n-myft-ui",
3
- "version": "28.2.0",
3
+ "version": "28.2.1",
4
4
  "description": "Client side component for interaction with myft",
5
5
  "main": "server.js",
6
6
  "scripts": {
@@ -65,6 +65,7 @@
65
65
  "karma-sinon": "^1.0.5",
66
66
  "karma-sinon-chai": "2.0.2",
67
67
  "karma-sourcemap-loader": "^0.3.7",
68
+ "karma-viewport": "^1.0.9",
68
69
  "karma-webpack": "^4.0.2",
69
70
  "lintspaces-cli": "^0.7.0",
70
71
  "lolex": "5.1.1",
@@ -0,0 +1,20 @@
1
+ const expect = require('chai').expect;
2
+ const isMobile = require('../../myft/ui/lib/is-mobile');
3
+
4
+ /* global viewport */
5
+
6
+ describe('IsMobile', function () {
7
+ afterEach(() => {
8
+ viewport.reset();
9
+ });
10
+
11
+ it('detects a desktop device', () => {
12
+ viewport.set(981, 800);
13
+ expect(isMobile()).to.be.false;
14
+ });
15
+
16
+ it('detects a mobile device', () => {
17
+ viewport.set(979, 800);
18
+ expect(isMobile()).to.be.true;
19
+ });
20
+ });