🐛 Fixed button URL suggestions not loading for contributors, editors and authors (#20416)
ref https://linear.app/tryghost/issue/SLO-127 - problem: when using a card with a button (Button, Email CTA, Header, Product), the Button URL suggestions fail to load for Contributors, Authors, and Editors - cause: Contributors, Authors and Editors don’t have permission to fetch offers, and this causes the entire list of button url suggestions to break - solution: if offers fail to fetch for any reason, the rest of the url suggestions for cards with a button is now still populated (i.e. offers URLs are ignored)
This commit is contained in:
parent
524fe6ee19
commit
1c972c7dd1
@ -83,6 +83,28 @@ export function decoratePostSearchResult(item, settings) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the URLs of all active offers
|
||||
* @returns {Promise<{label: string, value: string}[]>}
|
||||
*/
|
||||
export async function offerUrls() {
|
||||
let offers = [];
|
||||
|
||||
try {
|
||||
offers = await this.fetchOffersTask.perform();
|
||||
} catch (e) {
|
||||
// No-op: if offers are not available (e.g. missing permissions), return an empty array
|
||||
return [];
|
||||
}
|
||||
|
||||
return offers.map((offer) => {
|
||||
return {
|
||||
label: `Offer — ${offer.name}`,
|
||||
value: this.config.getSiteUrl(offer.code)
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
class ErrorHandler extends React.Component {
|
||||
state = {
|
||||
hasError: false
|
||||
@ -273,18 +295,6 @@ export default class KoenigLexicalEditor extends Component {
|
||||
};
|
||||
|
||||
const fetchAutocompleteLinks = async () => {
|
||||
let offers = [];
|
||||
try {
|
||||
offers = await this.fetchOffersTask.perform();
|
||||
} catch (e) {
|
||||
// Do not throw cancellation errors
|
||||
if (didCancel(e)) {
|
||||
return;
|
||||
}
|
||||
|
||||
throw e;
|
||||
}
|
||||
|
||||
const defaults = [
|
||||
{label: 'Homepage', value: window.location.origin + '/'},
|
||||
{label: 'Free signup', value: '#/portal/signup/free'}
|
||||
@ -329,12 +339,7 @@ export default class KoenigLexicalEditor extends Component {
|
||||
return [];
|
||||
};
|
||||
|
||||
const offersLinks = offers.toArray().map((offer) => {
|
||||
return {
|
||||
label: `Offer - ${offer.name}`,
|
||||
value: this.config.getSiteUrl(offer.code)
|
||||
};
|
||||
});
|
||||
const offersLinks = await offerUrls.call(this);
|
||||
|
||||
return [...defaults, ...memberLinks(), ...donationLink(), ...recommendationLink(), ...offersLinks];
|
||||
};
|
||||
|
@ -1,4 +1,5 @@
|
||||
import {decoratePostSearchResult} from 'ghost-admin/components/koenig-lexical-editor';
|
||||
import sinon from 'sinon';
|
||||
import {decoratePostSearchResult, offerUrls} from 'ghost-admin/components/koenig-lexical-editor';
|
||||
import {describe, it} from 'mocha';
|
||||
import {expect} from 'chai';
|
||||
|
||||
@ -67,4 +68,59 @@ describe('Unit: Component: koenig-lexical-editor', function () {
|
||||
expect(result.metaIconTitle).to.be.undefined;
|
||||
});
|
||||
});
|
||||
|
||||
describe('offersUrls', function () {
|
||||
let context;
|
||||
let performStub;
|
||||
|
||||
beforeEach(function () {
|
||||
context = {
|
||||
fetchOffersTask: {
|
||||
perform: () => {}
|
||||
},
|
||||
config: {
|
||||
getSiteUrl: code => `https://example.com?offer=${code}`
|
||||
}
|
||||
};
|
||||
|
||||
performStub = sinon.stub(context.fetchOffersTask, 'perform');
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
sinon.restore();
|
||||
});
|
||||
|
||||
it('returns an empty array if fetching offers gives no result', async function () {
|
||||
performStub.resolves([]);
|
||||
|
||||
const results = await offerUrls.call(context);
|
||||
|
||||
expect(performStub.callCount).to.equal(1);
|
||||
expect(results).to.deep.equal([]);
|
||||
});
|
||||
|
||||
it('returns an empty array if fetching offers fails', async function () {
|
||||
performStub.rejects(new Error('Failed to fetch offers'));
|
||||
|
||||
const results = await offerUrls.call(context);
|
||||
|
||||
expect(performStub.callCount).to.equal(1);
|
||||
expect(results).to.deep.equal([]);
|
||||
});
|
||||
|
||||
it(('returns an array of offers urls if fetching offers is successful'), async function () {
|
||||
performStub.resolves([
|
||||
{name: 'Yellow Thursday', code: 'yellow-thursday'},
|
||||
{name: 'Green Friday', code: 'green-friday'}
|
||||
]);
|
||||
|
||||
const results = await offerUrls.call(context);
|
||||
|
||||
expect(performStub.callCount).to.equal(1);
|
||||
expect(results).to.deep.equal([
|
||||
{label: 'Offer — Yellow Thursday', value: 'https://example.com?offer=yellow-thursday'},
|
||||
{label: 'Offer — Green Friday', value: 'https://example.com?offer=green-friday'}
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user