@financial-times/n-myft-ui 28.3.2 → 29.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -4,8 +4,8 @@
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
- {{#if flags.manageArticleLists}}data-myft-ui-save-new="manageArticleLists"{{/if}}
8
- {{#if flags.manageArticleLists}}data-myft-ui-save-new-config="{{#if flags.myftListPublicPrivateToggle}}showPublicToggle{{/if}}"{{/if}}>
7
+ {{#if @root.flags.manageArticleLists}}data-myft-ui-save-new="manageArticleLists"{{/if}}
8
+ {{#if @root.flags.manageArticleLists}}data-myft-ui-save-new-config="{{#if @root.flags.myftListPublicPrivateToggle}}showPublicToggle{{/if}},{{#if modal}}modal{{/if}}"{{/if}}>
9
9
  {{> n-myft-ui/components/csrf-token/input}}
10
10
  <div
11
11
  class="n-myft-ui__announcement o-normalise-visually-hidden"
@@ -20,7 +20,7 @@
20
20
  {{#if isSaved}}
21
21
  {{!-- 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 --}}
22
22
  title="{{#if title}}{{title}} is{{/if}} Saved to myFT"
23
- aria-label="{{#if title}}{{title}} is{{/if}} Saved to myFT"
23
+ aria-label="{{#if title}}{{title}} is{{/if}} Saved to myFT - Unsave"
24
24
  data-alternate-label="{{#if title}}Save {{title}} to myFT for later{{else}}Save this article to myFT for later{{/if}}"
25
25
  aria-pressed="true"
26
26
  {{else}}
package/myft/main.scss CHANGED
@@ -24,6 +24,17 @@ $spacing-unit: 20px;
24
24
  display: inline-block;
25
25
  }
26
26
 
27
+ // article lists modal
28
+ @include oForms($opts: (
29
+ 'elements': ('text', 'checkbox', 'toggle')
30
+ ));
31
+
32
+ @include oButtons($opts: (
33
+ 'sizes': ('big'),
34
+ 'types': ('primary', 'secondary'),
35
+ 'themes': ('inverse')
36
+ ));
37
+
27
38
  // experimental flash animation on header icon
28
39
  @include myftHeaderIconFlash;
29
40
 
@@ -174,196 +185,198 @@ $spacing-unit: 20px;
174
185
 
175
186
  }
176
187
 
177
- .share-nav {
178
- &.data-overlap-initialised {
179
- .o-overlay {
180
- transition: opacity 0.15s ease-in;
181
- opacity: 0;
182
- z-index: -1;
188
+ .myft-ui-create-list-variant-message {
189
+ border-radius: 10px;
190
+ border: 1px solid oColorsByName('black-5');
191
+ background: oColorsByName('white-80');
192
+
193
+ &-content {
194
+ display: flex;
195
+ flex-direction: column;
196
+
197
+ h3 {
198
+ margin: 0;
183
199
  }
184
200
  }
185
201
 
186
- .myft-ui-create-list-variant-message {
187
- border-radius: 10px;
188
- border: 1px solid oColorsByName('black-5');
189
- background: oColorsByName('white-80');
202
+ &-buttons {
203
+ text-align: center;
204
+ }
205
+ }
190
206
 
191
- &-content {
192
- display: flex;
193
- flex-direction: column;
207
+ .myft-ui-create-list-variant {
208
+ border-radius: 10px;
209
+ border: 1px solid oColorsByName('black-5');
210
+ background: oColorsByName('white-80');
194
211
 
195
- h3 {
196
- margin: 0;
197
- }
198
- }
212
+ .o-overlay__heading {
213
+ border-radius: 10px 10px 0 0;
214
+ background: oColorsByName('white-60');
215
+ @include oTypographySans($scale: 2);
216
+ color: oColorsByName('black-80');
217
+ }
199
218
 
200
- &-buttons {
201
- text-align: center;
202
- }
219
+ .o-overlay__content {
220
+ @include oTypographySans($scale: 0);
221
+ color: oColorsByName('black-80');
222
+ padding: 0;
203
223
  }
204
224
 
225
+ .o-overlay__title {
226
+ margin: 8px 14px 0 8px;
227
+ }
205
228
 
206
- .myft-ui-create-list-variant {
207
- border-radius: 10px;
208
- border: 1px solid oColorsByName('black-5');
209
- background: oColorsByName('white-80');
229
+ &-container {
230
+ display: block;
231
+ width: 340px;
232
+ top: 115.5px;
233
+ left: 50px;
234
+ }
210
235
 
211
- .o-overlay__heading {
212
- border-radius: 10px 10px 0 0;
213
- background: oColorsByName('white-60');
214
- @include oTypographySans($scale: 2);
215
- color: oColorsByName('black-80');
216
- }
236
+ &-add {
237
+ border: 0;
238
+ background: none;
239
+ @include oTypographySans($scale: 1, $weight: 'semibold');
240
+ color: oColorsByName('black-80');
217
241
 
218
- .o-overlay__content {
219
- @include oTypographySans($scale: 0);
220
- color: oColorsByName('black-80');
221
- padding: 0;
222
- }
242
+ padding-left: 0;
223
243
 
224
- .o-overlay__title {
225
- margin: 8px 14px 0 8px;
244
+ &:hover {
245
+ text-decoration: underline;
226
246
  }
227
247
 
228
- &-container {
229
- display: block;
230
- width: 340px;
231
- top: 115.5px;
232
- left: 50px;
248
+ &-collapsed::before {
249
+ content: '';
250
+ @include oIconsContent(
251
+ 'plus',
252
+ oColorsByName('black-80'),
253
+ 28,
254
+ $iconset-version: 1
255
+ );
256
+ vertical-align: middle;
257
+ margin-top: -2px;
258
+ margin-left: -8px;
233
259
  }
260
+ }
234
261
 
235
- &-add {
236
- border: 0;
237
- background: none;
238
- @include oTypographySans($scale: 1, $weight: 'semibold');
239
- color: oColorsByName('black-80');
240
-
241
- padding-left: 0;
242
-
243
- &:hover {
244
- text-decoration: underline;
245
- }
262
+ &-add-description {
263
+ margin: oSpacingByName('s1') 0;
264
+ }
246
265
 
247
- &-collapsed::before {
248
- content: '';
249
- @include oIconsContent(
250
- 'plus',
251
- oColorsByName('black-80'),
252
- 28,
253
- $iconset-version: 1
266
+ &-heading {
267
+ &::before {
268
+ content: '';
269
+ @include oIconsContent(
270
+ 'tick',
271
+ oColorsByName('teal'),
272
+ 32,
273
+ $iconset-version: 1
254
274
  );
255
275
  vertical-align: middle;
256
276
  margin-top: -2px;
257
- margin-left: -8px;
258
277
  }
259
278
  }
260
279
 
261
- &-add-description {
262
- margin: oSpacingByName('s1') 0;
263
- }
280
+ &-footer {
281
+ border-top: 1px solid oColorsByName('black-5');
282
+ padding: oSpacingByName('s4');
283
+ }
264
284
 
265
- &-heading {
266
- &::before {
267
- content: '';
268
- @include oIconsContent(
269
- 'tick',
270
- oColorsByName('teal'),
271
- 32,
272
- $iconset-version: 1
273
- );
274
- vertical-align: middle;
275
- margin-top: -2px;
276
- }
285
+ &-icon {
286
+ &::before {
287
+ content: "";
288
+ display: inline-block;
289
+ background-repeat: no-repeat;
290
+ background-size: contain;
291
+ background-position: 50%;
292
+ background-color: transparent;
293
+ background-image: url(https://www.ft.com/__origami/service/image/v2/images/raw/ftlogo-v1:brand-myft?source=next-article);
294
+ width: 42px;
295
+ height: 42px;
296
+ vertical-align: middle;
297
+ margin-top: -2px;
277
298
  }
278
299
 
279
- &-footer {
280
- border-top: 1px solid oColorsByName('black-5');
281
- padding: oSpacingByName('s4');
300
+ &-visually-hidden {
301
+ clip: rect(0 0 0 0);
302
+ clip-path: inset(50%);
303
+ height: 1px;
304
+ overflow: hidden;
305
+ position: absolute;
306
+ white-space: nowrap;
307
+ width: 1px;
308
+ }
282
309
  }
283
310
 
284
- &-icon {
285
- &::before {
286
- content: "";
287
- display: inline-block;
288
- background-repeat: no-repeat;
289
- background-size: contain;
290
- background-position: 50%;
291
- background-color: transparent;
292
- background-image: url(https://www.ft.com/__origami/service/image/v2/images/raw/ftlogo-v1:brand-myft?source=next-article);
293
- width: 42px;
294
- height: 42px;
295
- vertical-align: middle;
296
- margin-top: -2px;
297
- }
298
-
299
- &-visually-hidden {
300
- clip: rect(0 0 0 0);
301
- clip-path: inset(50%);
302
- height: 1px;
303
- overflow: hidden;
304
- position: absolute;
305
- white-space: nowrap;
306
- width: 1px;
307
- }
311
+ &-form {
312
+ $field-spacing: 's4';
313
+ display: flex;
314
+ flex-direction: column;
315
+ width: calc(100% - 32px);
316
+ gap: oSpacingByName($field-spacing);
317
+ padding: 0 oSpacingByName($field-spacing) oSpacingByName('s3');
318
+
319
+ & >* {
320
+ flex: 1 1 auto;
321
+ margin-bottom: 0;
308
322
  }
309
323
 
310
- &-form {
311
- $field-spacing: 's4';
312
- display: flex;
313
- flex-direction: column;
314
- width: calc(100% - 32px);
315
- gap: oSpacingByName($field-spacing);
316
- padding: 0 oSpacingByName($field-spacing) oSpacingByName('s3');
317
-
318
- & > * {
319
- flex: 1 1 auto;
320
- margin-bottom: 0;
321
- }
324
+ & >*.o-forms-field {
325
+ margin-bottom: 0;
326
+ }
322
327
 
323
- .o-forms-input {
324
- margin-top: 0;
325
- }
328
+ .o-forms-input {
329
+ margin-top: 0;
330
+ }
326
331
 
327
- &-toggle {
328
- position: absolute;
329
- }
332
+ &-toggle {
333
+ position: absolute;
334
+ }
330
335
 
331
- &-toggle-label::after {
332
- background-color: oColorsByName('white');
333
- }
336
+ &-toggle-label.o-forms-input__label::after {
337
+ background-color: oColorsByName('white');
338
+ }
334
339
 
335
- &-buttons {
336
- display: flex;
337
- justify-content: flex-end;
338
- @include oTypographySans($scale: 2);
339
- }
340
+ &-buttons {
341
+ display: flex;
342
+ justify-content: flex-end;
343
+ @include oTypographySans($scale: 2);
344
+ }
340
345
 
341
- &-public {
342
- max-width: 300px;
343
- padding: 0 3px;
344
- }
346
+ &-public {
347
+ max-width: 300px;
348
+ padding: 0 3px;
345
349
  }
350
+ }
346
351
 
347
- &-lists {
348
- padding: oSpacingByName('s4') oSpacingByName('s4') 0;
349
- @include oTypographySans($scale: 1);
350
- &-text {
351
- @include oTypographySans($weight: 'semibold');
352
- color: oColorsByName('black-80');
353
- margin-bottom: oSpacingByName('s3');
354
- }
355
- &-container {
356
- margin-top: 0;
357
- max-height: 92px;
358
- padding: 4px 2px;
359
- overflow-y: auto;
360
- @include oGridRespondTo($from: M) {
361
- max-height: 126px;
362
- }
352
+ &-lists {
353
+ padding: oSpacingByName('s4') oSpacingByName('s4') 0;
354
+ @include oTypographySans($scale: 1);
355
+ &-text {
356
+ @include oTypographySans($weight: 'semibold');
357
+ color: oColorsByName('black-80');
358
+ margin-bottom: oSpacingByName('s3');
359
+ }
360
+ &-container {
361
+ margin-top: 0;
362
+ max-height: 92px;
363
+ padding: 4px 2px;
364
+ overflow-y: auto;
365
+ @include oGridRespondTo($from: M) {
366
+ max-height: 126px;
363
367
  }
364
368
  }
365
369
  }
370
+ }
366
371
 
372
+ .share-nav {
373
+ &.data-overlap-initialised {
374
+ .o-overlay {
375
+ transition: opacity 0.15s ease-in;
376
+ opacity: 0;
377
+ z-index: -1;
378
+ }
379
+ }
367
380
  .myft-notification {
368
381
  background: oColorsByName('white-80');
369
382
  box-sizing: border-box;
@@ -14,7 +14,7 @@ let scrolledOnOpen;
14
14
  let listOverlayBottom;
15
15
 
16
16
  export default async function openSaveArticleToListVariant (contentId, options = {}) {
17
- const { name, showPublicToggle = false } = options;
17
+ const { name, showPublicToggle = false, modal = false } = options;
18
18
 
19
19
  function createList (newList, cb) {
20
20
  if(!newList || !newList.name) {
@@ -81,9 +81,9 @@ export default async function openSaveArticleToListVariant (contentId, options =
81
81
  const [listElement, refreshListElement, hideListElement, showListElement] = ListsElement(lists, addToList, removeFromList);
82
82
 
83
83
  createListOverlay = new Overlay(name, {
84
+ modal,
84
85
  html: contentElement,
85
86
  heading: { title: headingElement.outerHTML },
86
- modal: false,
87
87
  parentnode: isMobile() ? '.o-share--horizontal' : '.o-share--vertical',
88
88
  class: 'myft-ui-create-list-variant',
89
89
  });
@@ -112,11 +112,10 @@ export default async function openSaveArticleToListVariant (contentId, options =
112
112
 
113
113
  function openFormHandler () {
114
114
  hideListElement();
115
- const formElement = FormElement(createList, showPublicToggle, attachDescription, onFormListCreated, onFormCancel);
115
+ const formElement = FormElement(createList, showPublicToggle, attachDescription, onFormListCreated, onFormCancel, modal);
116
116
  const overlayContent = document.querySelector('.o-overlay__content');
117
117
  removeDescription();
118
118
  overlayContent.insertAdjacentElement('beforeend', formElement);
119
- formElement.elements[0].focus();
120
119
  }
121
120
 
122
121
  createListOverlay.open();
@@ -131,17 +130,20 @@ export default async function openSaveArticleToListVariant (contentId, options =
131
130
  overlayContent.insertAdjacentElement('afterbegin', listElement);
132
131
  }
133
132
 
134
- positionOverlay(data.currentTarget);
133
+ if (!modal) {
134
+ positionOverlay(data.currentTarget);
135
+
136
+ window.addEventListener('oViewport.resize', resizeHandler);
137
+ window.addEventListener('scroll', scrollHandler);
138
+ }
135
139
 
136
140
  listOverlayBottom = document.querySelector('.myft-ui-create-list-variant').getBoundingClientRect().bottom;
137
141
 
138
142
  restoreFormHandler();
139
143
 
140
- document.querySelector('.article-content').addEventListener('click', outsideClickHandler);
144
+ document.body.addEventListener('click', outsideClickHandler);
141
145
 
142
- window.addEventListener('scroll', scrollHandler);
143
146
 
144
- window.addEventListener('oViewport.resize', resizeHandler);
145
147
  });
146
148
 
147
149
  createListOverlay.wrapper.addEventListener('oOverlay.destroy', () => {
@@ -149,7 +151,7 @@ export default async function openSaveArticleToListVariant (contentId, options =
149
151
 
150
152
  window.removeEventListener('oViewport.resize', resizeHandler);
151
153
 
152
- document.querySelector('.article-content').removeEventListener('click', outsideClickHandler);
154
+ document.body.removeEventListener('click', outsideClickHandler);
153
155
  });
154
156
  }
155
157
 
@@ -163,12 +165,13 @@ function getResizeHandler (target) {
163
165
  };
164
166
  }
165
167
 
166
- function FormElement (createList, showPublicToggle, attachDescription, onListCreated, onCancel) {
168
+ function FormElement (createList, showPublicToggle, attachDescription, onListCreated, onCancel, modal=false) {
167
169
  const formString = `
168
170
  <form class="myft-ui-create-list-variant-form">
169
171
  <label class="myft-ui-create-list-variant-form-name o-forms-field">
170
172
  <span class="o-forms-input o-forms-input--text">
171
- <input class="myft-ui-create-list-variant-text" type="text" name="list-name" aria-label="List name">
173
+ <input class="myft-ui-create-list-variant-text" type="text" name="list-name">
174
+ List name
172
175
  </span>
173
176
  </label>
174
177
 
@@ -218,7 +221,9 @@ function FormElement (createList, showPublicToggle, attachDescription, onListCre
218
221
  createList(newList, ((contentId, createdList) => {
219
222
  triggerCreateListEvent(contentId, createdList.uuid);
220
223
  triggerAddToListEvent(contentId, createdList.uuid);
221
- positionOverlay(createListOverlay.wrapper);
224
+ if (!modal) {
225
+ positionOverlay(createListOverlay.wrapper);
226
+ }
222
227
  onListCreated();
223
228
  }));
224
229
  formElement.remove();
@@ -258,10 +263,15 @@ function ContentElement (hasDescription, onClick) {
258
263
 
259
264
  const content = `
260
265
  <div class="myft-ui-create-list-variant-footer">
261
- <button class="myft-ui-create-list-variant-add myft-ui-create-list-variant-add-collapsed" data-trackable="add-to-new-list" text="add to new list">Add to a new list</button>
266
+ <button class="myft-ui-create-list-variant-add myft-ui-create-list-variant-add-collapsed" aria-expanded=false data-trackable="add-to-new-list" text="add to new list">Add to a new list</button>
262
267
  ${hasDescription ? `
263
268
  ${description}
264
269
  ` : ''}
270
+ <span
271
+ class="myft-ui-create-list-variant-announcement o-normalise-visually-hidden"
272
+ role="region"
273
+ aria-live="assertive"
274
+ />
265
275
  </div>
266
276
  `;
267
277
 
@@ -284,11 +294,13 @@ function ContentElement (hasDescription, onClick) {
284
294
 
285
295
  function restoreFormHandler () {
286
296
  contentElement.querySelector('.myft-ui-create-list-variant-add').classList.add('myft-ui-create-list-variant-add-collapsed');
297
+ contentElement.querySelector('.myft-ui-create-list-variant-add').setAttribute('aria-expanded', false);
287
298
  return contentElement.addEventListener('click', clickHandler, { once: true });
288
299
  }
289
300
 
290
301
  function clickHandler (event) {
291
302
  contentElement.querySelector('.myft-ui-create-list-variant-add').classList.remove('myft-ui-create-list-variant-add-collapsed');
303
+ contentElement.querySelector('.myft-ui-create-list-variant-add').setAttribute('aria-expanded', true);
292
304
  onClick(event);
293
305
  }
294
306
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@financial-times/n-myft-ui",
3
- "version": "28.3.2",
3
+ "version": "29.0.0",
4
4
  "description": "Client side component for interaction with myft",
5
5
  "main": "server.js",
6
6
  "scripts": {