@financial-times/n-conversion-forms 44.5.2 → 45.0.0

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "branch": "",
3
3
  "repo": "n-conversion-forms",
4
- "version": "3d7277f162178bab7f179119d5b1ab604450e546",
5
- "tag": "v44.5.2",
6
- "buildNumber": "17578"
4
+ "version": "af67cefb78210e23aab60a7c27b2a97b79880e2e",
5
+ "tag": "v45.0.0",
6
+ "buildNumber": "17972"
7
7
  }
@@ -1,5 +1,55 @@
1
1
  // Jest Snapshot v1, https://goo.gl/fbAQLP
2
2
 
3
+ exports[`Confirmation renders appropriately if is 'Evergreen' subscription term type 1`] = `
4
+ <div class="ncf ncf__wrapper">
5
+ <div class="ncf__center">
6
+ <div class="ncf__icon ncf__icon--tick ncf__icon--large">
7
+ </div>
8
+ <p class="ncf__paragraph--reduced-padding ncf__paragraph--subscription-confirmation">
9
+ You are now subscribed to:
10
+ </p>
11
+ <h1 class="ncf__header ncf__header--confirmation">
12
+ </h1>
13
+ </div>
14
+ <p class="ncf__paragraph">
15
+ We’ve sent confirmation to your email. Make sure you check your spam folder if you don’t receive it.
16
+ </p>
17
+ <p class="ncf__paragraph">
18
+ Here’s a summary of your subscription:
19
+ </p>
20
+ <div class="ncf__headed-paragraph">
21
+ <h3 class="ncf__header">
22
+ Something not right?
23
+ </h3>
24
+ <p class="ncf__paragraph o3-type-body-base">
25
+ Go to your
26
+ <a href="https://www.ft.com/myaccount/personal-details"
27
+ target="_blank"
28
+ rel="noopener noreferrer"
29
+ data-trackable="yourAccount"
30
+ >
31
+ account settings
32
+ </a>
33
+ to view or edit your account. If you need to get in touch call us on
34
+ <a href="tel:+442077556248">
35
+ +44 (0) 207 755 6248
36
+ </a>
37
+ . Or contact us for additional support.
38
+ </p>
39
+ </div>
40
+ <p class="ncf__paragraph o3-type-body-base">
41
+ We will automatically renew your subscription using the payment method provided unless you cancel before your renewal date. See our
42
+ <a href="http://help.ft.com/help/legal-privacy/terms-conditions/"
43
+ target="_top"
44
+ rel="noopener"
45
+ >
46
+ Terms &amp; Conditions
47
+ </a>
48
+ for details on how to cancel.
49
+ </p>
50
+ </div>
51
+ `;
52
+
3
53
  exports[`Confirmation renders appropriately if is 'Termed' subscription term type 1`] = `
4
54
  <div class="ncf ncf__wrapper">
5
55
  <div class="ncf__center">
@@ -71,9 +71,6 @@ exports[`GraduationDate renders with default props 1`] = `
71
71
  name="graduationDateYear"
72
72
  aria-required="false"
73
73
  >
74
- <option value="2021">
75
- 2021
76
- </option>
77
74
  <option value="2022">
78
75
  2022
79
76
  </option>
@@ -98,6 +95,9 @@ exports[`GraduationDate renders with default props 1`] = `
98
95
  <option value="2029">
99
96
  2029
100
97
  </option>
98
+ <option value="2030">
99
+ 2030
100
+ </option>
101
101
  </select>
102
102
  </span>
103
103
  </div>
@@ -8,7 +8,7 @@ export function Confirmation({
8
8
  isTrial = false,
9
9
  isB2cPartnership = false,
10
10
  b2cPartnershipCopy = [],
11
- isTermedSubscriptionTermType = false,
11
+ isEvergreenSubscriptionTermType = true,
12
12
  offer = '',
13
13
  email = EMAIL_DEFAULT_TEXT,
14
14
  details = null,
@@ -114,7 +114,7 @@ export function Confirmation({
114
114
  </p>
115
115
  </div>
116
116
  <p className="ncf__paragraph o3-type-body-base">
117
- {!isTermedSubscriptionTermType
117
+ {isEvergreenSubscriptionTermType
118
118
  ? 'We will automatically renew your subscription using the payment method provided unless you cancel before your renewal date. '
119
119
  : ''}
120
120
  {'See our '}
@@ -136,7 +136,7 @@ Confirmation.propTypes = {
136
136
  isTrial: PropTypes.bool,
137
137
  isB2cPartnership: PropTypes.bool,
138
138
  b2cPartnershipCopy: PropTypes.array,
139
- isTermedSubscriptionTermType: PropTypes.bool,
139
+ isEvergreenSubscriptionTermType: PropTypes.bool,
140
140
  offer: PropTypes.string.isRequired,
141
141
  email: PropTypes.string,
142
142
  details: PropTypes.arrayOf(
@@ -29,8 +29,20 @@ describe('Confirmation', () => {
29
29
  expect(Confirmation).toRenderCorrectly(props);
30
30
  });
31
31
 
32
+ it("renders appropriately if is 'Evergreen' subscription term type", () => {
33
+ const props = {
34
+ isEvergreenSubscriptionTermType: true,
35
+ isTermedSubscriptionTermType: false,
36
+ };
37
+
38
+ expect(Confirmation).toRenderCorrectly(props);
39
+ });
40
+
32
41
  it("renders appropriately if is 'Termed' subscription term type", () => {
33
- const props = { isTermedSubscriptionTermType: true };
42
+ const props = {
43
+ isEvergreenSubscriptionTermType: false,
44
+ isTermedSubscriptionTermType: true,
45
+ };
34
46
 
35
47
  expect(Confirmation).toRenderCorrectly(props);
36
48
  });
@@ -13,6 +13,7 @@ export function PaymentTerm({
13
13
  largePrice = false,
14
14
  optionsInARow = false,
15
15
  billingCountry = '',
16
+ isEvergreenSubscriptionTermType = true,
16
17
  isTermedSubscriptionTermType = false,
17
18
  }) {
18
19
  /**
@@ -162,7 +163,6 @@ export function PaymentTerm({
162
163
  </span>
163
164
  ),
164
165
  renewsText: (renewalPeriod) =>
165
- !isTermedSubscriptionTermType &&
166
166
  Boolean(renewalPeriod) && (
167
167
  <p className="ncf__payment-term__renews-text">
168
168
  Renews every {renewalPeriod} unless cancelled
@@ -228,7 +228,8 @@ export function PaymentTerm({
228
228
  <div className="ncf__payment-term__description">
229
229
  {nameMap[option.name].price(option.price)}
230
230
  {nameMap[option.name].monthlyPrice(option.monthlyPrice)}
231
- {nameMap[option.name].renewsText()}
231
+ {isEvergreenSubscriptionTermType &&
232
+ nameMap[option.name].renewsText()}
232
233
  {/* Remove this discount text temporarily in favour of monthly price */}
233
234
  {/* <br />Save up to 25% when you pay annually */}
234
235
  </div>
@@ -246,7 +247,8 @@ export function PaymentTerm({
246
247
  option.value
247
248
  )
248
249
  )}
249
- {nameMap['custom'].renewsText(getTimeFromPeriod(option.value))}
250
+ {isEvergreenSubscriptionTermType &&
251
+ nameMap['custom'].renewsText(getTimeFromPeriod(option.value))}
250
252
  </div>
251
253
  ) : (
252
254
  <div>
@@ -342,20 +344,7 @@ export function PaymentTerm({
342
344
 
343
345
  {showLegal && (
344
346
  <div className="ncf__payment-term__legal">
345
- {isTermedSubscriptionTermType ? (
346
- <p className="o3-type-body-base">
347
- Find out more about our cancellation policy in our{' '}
348
- <a
349
- href="https://help.ft.com/legal-privacy/terms-and-conditions/"
350
- title="FT Legal Terms and Conditions help page"
351
- target="_blank"
352
- rel="noopener noreferrer"
353
- >
354
- Terms &amp; Conditions
355
- </a>
356
- .
357
- </p>
358
- ) : (
347
+ {isEvergreenSubscriptionTermType && (
359
348
  <React.Fragment>
360
349
  <p>
361
350
  With all subscription types, we will automatically renew your
@@ -378,6 +367,21 @@ export function PaymentTerm({
378
367
  </p>
379
368
  </React.Fragment>
380
369
  )}
370
+
371
+ {isTermedSubscriptionTermType && (
372
+ <p className="o3-type-body-base">
373
+ Find out more about our cancellation policy in our{' '}
374
+ <a
375
+ href="https://help.ft.com/legal-privacy/terms-and-conditions/"
376
+ title="FT Legal Terms and Conditions help page"
377
+ target="_blank"
378
+ rel="noopener noreferrer"
379
+ >
380
+ Terms &amp; Conditions
381
+ </a>
382
+ .
383
+ </p>
384
+ )}
381
385
  </div>
382
386
  )}
383
387
  </div>
@@ -411,6 +415,7 @@ PaymentTerm.propTypes = {
411
415
  fulfilmentOption: PropTypes.string,
412
416
  })
413
417
  ),
418
+ isEvergreenSubscriptionTermType: PropTypes.bool,
414
419
  isTermedSubscriptionTermType: PropTypes.bool,
415
420
  showLegal: PropTypes.bool,
416
421
  largePrice: PropTypes.bool,
@@ -117,7 +117,75 @@ describe('PaymentTerm', () => {
117
117
  });
118
118
  });
119
119
 
120
- describe('given isTermedSubscriptionTermType is true', () => {
120
+ describe('isEvergreenSubscriptionTermType is true; isTermedSubscriptionTermType is false', () => {
121
+ describe('pre-defined option name (i.e. frequency)', () => {
122
+ const ANNUAL_FREQUENCY = 'annual';
123
+ const QUARTERLY_FREQUENCY = 'quarterly';
124
+ const MONTHLY_FREQUENCY = 'monthly';
125
+
126
+ [ANNUAL_FREQUENCY, QUARTERLY_FREQUENCY, MONTHLY_FREQUENCY].forEach(
127
+ (frequency) => {
128
+ const FREQUENCY_TO_RENEWAL_TEXT_MAP = {
129
+ [ANNUAL_FREQUENCY]: 'Renews annually unless cancelled',
130
+ [QUARTERLY_FREQUENCY]: 'Renews quarterly unless cancelled',
131
+ [MONTHLY_FREQUENCY]: 'Renews monthly unless cancelled',
132
+ };
133
+
134
+ describe(`${frequency} option`, () => {
135
+ const options = [
136
+ {
137
+ name: frequency,
138
+ value: frequency,
139
+ price: '£20.00',
140
+ },
141
+ ];
142
+
143
+ it(`renders '${FREQUENCY_TO_RENEWAL_TEXT_MAP[frequency]}' when true`, () => {
144
+ const wrapper = shallow(
145
+ <PaymentTerm
146
+ options={options}
147
+ isEvergreenSubscriptionTermType={true}
148
+ isTermedSubscriptionTermType={false}
149
+ />
150
+ );
151
+ expect(
152
+ wrapper.find('.ncf__payment-term__renews-text').exists()
153
+ ).toBe(true);
154
+ expect(
155
+ wrapper.find('.ncf__payment-term__renews-text').text()
156
+ ).toBe(FREQUENCY_TO_RENEWAL_TEXT_MAP[frequency]);
157
+ });
158
+ });
159
+ }
160
+ );
161
+ });
162
+
163
+ describe('custom option with period provided', () => {
164
+ const options = [
165
+ {
166
+ price: '£50.00',
167
+ amount: '50.00',
168
+ currency: 'GBP',
169
+ value: 'P6M',
170
+ },
171
+ ];
172
+
173
+ it('renders renewal text for custom terms that specify a period', () => {
174
+ const wrapper = shallow(
175
+ <PaymentTerm
176
+ options={options}
177
+ isEvergreenSubscriptionTermType={true}
178
+ isTermedSubscriptionTermType={false}
179
+ />
180
+ );
181
+ expect(wrapper.find('.ncf__payment-term__renews-text').text()).toBe(
182
+ 'Renews every 6 months unless cancelled'
183
+ );
184
+ });
185
+ });
186
+ });
187
+
188
+ describe('isEvergreenSubscriptionTermType is false; isTermedSubscriptionTermType is true', () => {
121
189
  describe('options include duration expressed in weeks', () => {
122
190
  const options = [
123
191
  {
@@ -127,7 +195,11 @@ describe('PaymentTerm', () => {
127
195
  },
128
196
  ];
129
197
  const wrapper = shallow(
130
- <PaymentTerm options={options} isTermedSubscriptionTermType={true} />
198
+ <PaymentTerm
199
+ options={options}
200
+ isEvergreenSubscriptionTermType={false}
201
+ isTermedSubscriptionTermType={true}
202
+ />
131
203
  );
132
204
 
133
205
  it('renders subscription term as title', () => {
@@ -154,7 +226,11 @@ describe('PaymentTerm', () => {
154
226
  },
155
227
  ];
156
228
  const wrapper = shallow(
157
- <PaymentTerm options={options} isTermedSubscriptionTermType={true} />
229
+ <PaymentTerm
230
+ options={options}
231
+ isEvergreenSubscriptionTermType={false}
232
+ isTermedSubscriptionTermType={true}
233
+ />
158
234
  );
159
235
 
160
236
  it('renders subscription term as title', () => {
@@ -171,6 +247,29 @@ describe('PaymentTerm', () => {
171
247
  );
172
248
  });
173
249
  });
250
+
251
+ describe('renewal text', () => {
252
+ const options = [
253
+ {
254
+ price: '£19.00',
255
+ amount: '19.00',
256
+ value: 'P8W',
257
+ },
258
+ ];
259
+
260
+ it('does not render renewal text for custom terms', () => {
261
+ const wrapper = shallow(
262
+ <PaymentTerm
263
+ options={options}
264
+ isEvergreenSubscriptionTermType={false}
265
+ isTermedSubscriptionTermType={true}
266
+ />
267
+ );
268
+ expect(wrapper.find('.ncf__payment-term__renews-text').exists()).toBe(
269
+ false
270
+ );
271
+ });
272
+ });
174
273
  });
175
274
 
176
275
  describe('getDisplayName', () => {
@@ -18,8 +18,8 @@ function Confirmation(_ref) {
18
18
  isB2cPartnership = _ref$isB2cPartnership === void 0 ? false : _ref$isB2cPartnership,
19
19
  _ref$b2cPartnershipCo = _ref.b2cPartnershipCopy,
20
20
  b2cPartnershipCopy = _ref$b2cPartnershipCo === void 0 ? [] : _ref$b2cPartnershipCo,
21
- _ref$isTermedSubscrip = _ref.isTermedSubscriptionTermType,
22
- isTermedSubscriptionTermType = _ref$isTermedSubscrip === void 0 ? false : _ref$isTermedSubscrip,
21
+ _ref$isEvergreenSubsc = _ref.isEvergreenSubscriptionTermType,
22
+ isEvergreenSubscriptionTermType = _ref$isEvergreenSubsc === void 0 ? true : _ref$isEvergreenSubsc,
23
23
  _ref$offer = _ref.offer,
24
24
  offer = _ref$offer === void 0 ? '' : _ref$offer,
25
25
  _ref$email = _ref.email,
@@ -95,7 +95,7 @@ function Confirmation(_ref) {
95
95
  href: "tel:+442077556248"
96
96
  }, "+44 (0) 207 755 6248"), ". Or contact us for additional support.")), /*#__PURE__*/_react["default"].createElement("p", {
97
97
  className: "ncf__paragraph o3-type-body-base"
98
- }, !isTermedSubscriptionTermType ? 'We will automatically renew your subscription using the payment method provided unless you cancel before your renewal date. ' : '', 'See our ', /*#__PURE__*/_react["default"].createElement("a", {
98
+ }, isEvergreenSubscriptionTermType ? 'We will automatically renew your subscription using the payment method provided unless you cancel before your renewal date. ' : '', 'See our ', /*#__PURE__*/_react["default"].createElement("a", {
99
99
  href: "http://help.ft.com/help/legal-privacy/terms-conditions/",
100
100
  target: "_top",
101
101
  rel: "noopener"
@@ -105,7 +105,7 @@ Confirmation.propTypes = {
105
105
  isTrial: _propTypes["default"].bool,
106
106
  isB2cPartnership: _propTypes["default"].bool,
107
107
  b2cPartnershipCopy: _propTypes["default"].array,
108
- isTermedSubscriptionTermType: _propTypes["default"].bool,
108
+ isEvergreenSubscriptionTermType: _propTypes["default"].bool,
109
109
  offer: _propTypes["default"].string.isRequired,
110
110
  email: _propTypes["default"].string,
111
111
  details: _propTypes["default"].arrayOf(_propTypes["default"].shape({
@@ -31,6 +31,8 @@ function PaymentTerm(_ref) {
31
31
  optionsInARow = _ref$optionsInARow === void 0 ? false : _ref$optionsInARow,
32
32
  _ref$billingCountry = _ref.billingCountry,
33
33
  billingCountry = _ref$billingCountry === void 0 ? '' : _ref$billingCountry,
34
+ _ref$isEvergreenSubsc = _ref.isEvergreenSubscriptionTermType,
35
+ isEvergreenSubscriptionTermType = _ref$isEvergreenSubsc === void 0 ? true : _ref$isEvergreenSubsc,
34
36
  _ref$isTermedSubscrip = _ref.isTermedSubscriptionTermType,
35
37
  isTermedSubscriptionTermType = _ref$isTermedSubscrip === void 0 ? false : _ref$isTermedSubscrip;
36
38
  /**
@@ -160,7 +162,7 @@ function PaymentTerm(_ref) {
160
162
  }, _monthlyPrice), ' ', "per month");
161
163
  },
162
164
  renewsText: function renewsText(renewalPeriod) {
163
- return !isTermedSubscriptionTermType && Boolean(renewalPeriod) && /*#__PURE__*/_react["default"].createElement("p", {
165
+ return Boolean(renewalPeriod) && /*#__PURE__*/_react["default"].createElement("p", {
164
166
  className: "ncf__payment-term__renews-text"
165
167
  }, "Renews every ", renewalPeriod, " unless cancelled");
166
168
  }
@@ -197,12 +199,12 @@ function PaymentTerm(_ref) {
197
199
  className: "ncf__payment-term__trial-price"
198
200
  }, option.trialPrice), /*#__PURE__*/_react["default"].createElement("br", null), nameMap[option.name] && nameMap[option.name].trialPrice(option.price)) : /*#__PURE__*/_react["default"].createElement(_react["default"].Fragment, null, nameMap[option.name] ? /*#__PURE__*/_react["default"].createElement("div", {
199
201
  className: "ncf__payment-term__description"
200
- }, nameMap[option.name].price(option.price), nameMap[option.name].monthlyPrice(option.monthlyPrice), nameMap[option.name].renewsText()) :
202
+ }, nameMap[option.name].price(option.price), nameMap[option.name].monthlyPrice(option.monthlyPrice), isEvergreenSubscriptionTermType && nameMap[option.name].renewsText()) :
201
203
  // this should cover the cases different than annual, quarterly and monthly
202
204
  // for those containing period on option.value, render custom template, for the rest keep legacy render
203
205
  isValidPeriod(option.value) ? /*#__PURE__*/_react["default"].createElement("div", {
204
206
  className: "ncf__payment-term__description"
205
- }, nameMap['custom'].price(option.price), nameMap['custom'].monthlyPrice(option.monthlyPrice && option.monthlyPrice !== '0' ? Number(option.monthlyPrice) : getMonthlyPriceFromPeriod(option.amount, option.currency, option.value)), nameMap['custom'].renewsText(getTimeFromPeriod(option.value))) : /*#__PURE__*/_react["default"].createElement("div", null, /*#__PURE__*/_react["default"].createElement("span", {
207
+ }, nameMap['custom'].price(option.price), nameMap['custom'].monthlyPrice(option.monthlyPrice && option.monthlyPrice !== '0' ? Number(option.monthlyPrice) : getMonthlyPriceFromPeriod(option.amount, option.currency, option.value)), isEvergreenSubscriptionTermType && nameMap['custom'].renewsText(getTimeFromPeriod(option.value))) : /*#__PURE__*/_react["default"].createElement("div", null, /*#__PURE__*/_react["default"].createElement("span", {
206
208
  className: largePrice ? 'ncf__payment-term__large-price' : ''
207
209
  }, option.price), option.chargeOnText && /*#__PURE__*/_react["default"].createElement("p", {
208
210
  className: "ncf__payment-term__charge-on-text"
@@ -256,21 +258,21 @@ function PaymentTerm(_ref) {
256
258
  return createPaymentTerm(option);
257
259
  })), showLegal && /*#__PURE__*/_react["default"].createElement("div", {
258
260
  className: "ncf__payment-term__legal"
259
- }, isTermedSubscriptionTermType ? /*#__PURE__*/_react["default"].createElement("p", {
261
+ }, isEvergreenSubscriptionTermType && /*#__PURE__*/_react["default"].createElement(_react["default"].Fragment, null, /*#__PURE__*/_react["default"].createElement("p", null, "With all subscription types, we will automatically renew your subscription using the payment method provided unless you cancel before your renewal date."), /*#__PURE__*/_react["default"].createElement("p", {
260
262
  className: "o3-type-body-base"
261
- }, "Find out more about our cancellation policy in our", ' ', /*#__PURE__*/_react["default"].createElement("a", {
263
+ }, "We will notify you at least 14 days in advance of any changes to the price in your subscription that would apply upon next renewal. Find out more about our cancellation policy in our", ' ', /*#__PURE__*/_react["default"].createElement("a", {
262
264
  href: "https://help.ft.com/legal-privacy/terms-and-conditions/",
263
265
  title: "FT Legal Terms and Conditions help page",
264
266
  target: "_blank",
265
267
  rel: "noopener noreferrer"
266
- }, "Terms & Conditions"), ".") : /*#__PURE__*/_react["default"].createElement(_react["default"].Fragment, null, /*#__PURE__*/_react["default"].createElement("p", null, "With all subscription types, we will automatically renew your subscription using the payment method provided unless you cancel before your renewal date."), /*#__PURE__*/_react["default"].createElement("p", {
268
+ }, "Terms & Conditions"), ".")), isTermedSubscriptionTermType && /*#__PURE__*/_react["default"].createElement("p", {
267
269
  className: "o3-type-body-base"
268
- }, "We will notify you at least 14 days in advance of any changes to the price in your subscription that would apply upon next renewal. Find out more about our cancellation policy in our", ' ', /*#__PURE__*/_react["default"].createElement("a", {
270
+ }, "Find out more about our cancellation policy in our", ' ', /*#__PURE__*/_react["default"].createElement("a", {
269
271
  href: "https://help.ft.com/legal-privacy/terms-and-conditions/",
270
272
  title: "FT Legal Terms and Conditions help page",
271
273
  target: "_blank",
272
274
  rel: "noopener noreferrer"
273
- }, "Terms & Conditions"), "."))));
275
+ }, "Terms & Conditions"), ".")));
274
276
  }
275
277
  PaymentTerm.propTypes = {
276
278
  fieldId: _propTypes["default"].string,
@@ -297,6 +299,7 @@ PaymentTerm.propTypes = {
297
299
  chargeOnText: _propTypes["default"].string,
298
300
  fulfilmentOption: _propTypes["default"].string
299
301
  })),
302
+ isEvergreenSubscriptionTermType: _propTypes["default"].bool,
300
303
  isTermedSubscriptionTermType: _propTypes["default"].bool,
301
304
  showLegal: _propTypes["default"].bool,
302
305
  largePrice: _propTypes["default"].bool,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@financial-times/n-conversion-forms",
3
- "version": "44.5.2",
3
+ "version": "45.0.0",
4
4
  "description": "Containing jsx components and styles for forms included on Accounts and Acquisition apps (next-signup, next-profile, next-retention, etc).",
5
5
  "main": "./dist/index.js",
6
6
  "scripts": {
@@ -59,6 +59,15 @@
59
59
  label {
60
60
  float: none;
61
61
  width: 100%;
62
+ height: 100%;
63
+ }
64
+
65
+ span {
66
+ max-height: none;
67
+
68
+ @include oGridRespondTo($from: L) {
69
+ line-height: var(--o3-spacing-2xs);
70
+ }
62
71
  }
63
72
  }
64
73
 
@@ -66,7 +75,7 @@
66
75
  display: flex;
67
76
  flex-direction: column;
68
77
  justify-content: space-evenly;
69
- align-items: baseline;
78
+ align-items: stretch;
70
79
  margin-bottom: var(--o3-spacing-3xs);
71
80
 
72
81
  @include oGridRespondTo($from: L) {