mirror of
https://github.com/gristlabs/grist-core.git
synced 2024-10-27 20:44:07 +00:00
Add description and icon to preview URLs for non-document resources #1242
When pasting a URL in some app or website that allows previewing the link, for other resources than documents, you were offered an irrelevant description. This patches aims to give a generic description of what is Grist.
This commit is contained in:
parent
346c9fc0b4
commit
b2d0803cfe
@ -159,8 +159,8 @@ export function makeSendAppPage({ server, staticDir, tag, testLogin, baseDomain
|
|||||||
const staticTag = options.tag || tag;
|
const staticTag = options.tag || tag;
|
||||||
// If boot tag is used, serve assets locally, otherwise respect
|
// If boot tag is used, serve assets locally, otherwise respect
|
||||||
// APP_STATIC_URL.
|
// APP_STATIC_URL.
|
||||||
const staticOrigin = staticTag === 'boot' ? '' : (process.env.APP_STATIC_URL || '');
|
const staticOrigin = staticTag === 'boot' ? '' : (process.env.APP_STATIC_URL || config.homeUrl || '');
|
||||||
const staticBaseUrl = `${staticOrigin}/v/${staticTag}/`;
|
const staticBaseUrl = `${staticOrigin.replace(/\/*$/, '')}/v/${staticTag}/`;
|
||||||
const customHeadHtmlSnippet = server.create.getExtraHeadHtml?.() ?? "";
|
const customHeadHtmlSnippet = server.create.getExtraHeadHtml?.() ?? "";
|
||||||
const warning = testLogin ? "<div class=\"dev_warning\">Authentication is not enforced</div>" : "";
|
const warning = testLogin ? "<div class=\"dev_warning\">Authentication is not enforced</div>" : "";
|
||||||
// Preload all languages that will be used or are requested by client.
|
// Preload all languages that will be used or are requested by client.
|
||||||
@ -174,8 +174,8 @@ export function makeSendAppPage({ server, staticDir, tag, testLogin, baseDomain
|
|||||||
).join('\n');
|
).join('\n');
|
||||||
const content = fileContent
|
const content = fileContent
|
||||||
.replace("<!-- INSERT WARNING -->", warning)
|
.replace("<!-- INSERT WARNING -->", warning)
|
||||||
.replace("<!-- INSERT TITLE -->", getPageTitle(req, config))
|
.replace("<!-- INSERT TITLE -->", getPageTitle(config) ?? (translate(req, 'Loading') + "..."))
|
||||||
.replace("<!-- INSERT META -->", getPageMetadataHtmlSnippet(config))
|
.replace("<!-- INSERT META -->", getPageMetadataHtmlSnippet(req, config, staticBaseUrl))
|
||||||
.replace("<!-- INSERT TITLE SUFFIX -->", getPageTitleSuffix(server.getGristConfig()))
|
.replace("<!-- INSERT TITLE SUFFIX -->", getPageTitleSuffix(server.getGristConfig()))
|
||||||
.replace("<!-- INSERT BASE -->", `<base href="${staticBaseUrl}">` + tagManagerSnippet)
|
.replace("<!-- INSERT BASE -->", `<base href="${staticBaseUrl}">` + tagManagerSnippet)
|
||||||
.replace("<!-- INSERT LOCALE -->", preloads)
|
.replace("<!-- INSERT LOCALE -->", preloads)
|
||||||
@ -271,11 +271,10 @@ function configuredPageTitleSuffix() {
|
|||||||
*
|
*
|
||||||
* Note: The string returned is escaped and safe to insert into HTML.
|
* Note: The string returned is escaped and safe to insert into HTML.
|
||||||
*/
|
*/
|
||||||
function getPageTitle(req: express.Request, config: GristLoadConfig): string {
|
function getPageTitle(config: GristLoadConfig): string|null {
|
||||||
const maybeDoc = getDocFromConfig(config);
|
const maybeDoc = getDocFromConfig(config);
|
||||||
if (!maybeDoc) { return translate(req, 'Loading') + "..."; }
|
|
||||||
|
|
||||||
return handlebars.Utils.escapeExpression(maybeDoc.name);
|
return maybeDoc && handlebars.Utils.escapeExpression(maybeDoc.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -286,25 +285,26 @@ function getPageTitle(req: express.Request, config: GristLoadConfig): string {
|
|||||||
*
|
*
|
||||||
* Note: The string returned is escaped and safe to insert into HTML.
|
* Note: The string returned is escaped and safe to insert into HTML.
|
||||||
*/
|
*/
|
||||||
function getPageMetadataHtmlSnippet(config: GristLoadConfig): string {
|
function getPageMetadataHtmlSnippet(req: express.Request, config: GristLoadConfig, staticBaseUrl: string): string {
|
||||||
const metadataElements: string[] = [];
|
const metadataElements: string[] = [];
|
||||||
const maybeDoc = getDocFromConfig(config);
|
const maybeDoc = getDocFromConfig(config);
|
||||||
|
|
||||||
const description = maybeDoc?.options?.description;
|
const description = maybeDoc?.options?.description ?? String(translate(req, 'gristMetaDescription'));
|
||||||
if (description) {
|
const escapedDescription = handlebars.Utils.escapeExpression(description);
|
||||||
const content = handlebars.Utils.escapeExpression(description);
|
metadataElements.push(`<meta name="description" content="${escapedDescription}">`);
|
||||||
metadataElements.push(`<meta name="description" content="${content}">`);
|
metadataElements.push(`<meta property="og:description" content="${escapedDescription}">`);
|
||||||
metadataElements.push(`<meta property="og:description" content="${content}">`);
|
metadataElements.push(`<meta name="twitter:description" content="${escapedDescription}">`);
|
||||||
metadataElements.push(`<meta name="twitter:description" content="${content}">`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const icon = maybeDoc?.options?.icon;
|
const icon = maybeDoc?.options?.icon ?? new URL('img/icon-grist.png', staticBaseUrl).href;
|
||||||
if (icon) {
|
const escapedIcon = handlebars.Utils.escapeExpression(icon);
|
||||||
const content = handlebars.Utils.escapeExpression(icon);
|
metadataElements.push(`<meta name="thumbnail" content="${escapedIcon}">`);
|
||||||
metadataElements.push(`<meta name="thumbnail" content="${content}">`);
|
metadataElements.push(`<meta property="og:image" content="${escapedIcon}">`);
|
||||||
metadataElements.push(`<meta property="og:image" content="${content}">`);
|
metadataElements.push(`<meta name="twitter:image" content="${escapedIcon}">`);
|
||||||
metadataElements.push(`<meta name="twitter:image" content="${content}">`);
|
|
||||||
}
|
const title = (getPageTitle(config) ?? translate(req, 'Welcome')) + getPageTitleSuffix(config);
|
||||||
|
// NB: We don't generate the content of the <title> tag here.
|
||||||
|
metadataElements.push(`<meta property="og:title" content="${title}">`);
|
||||||
|
metadataElements.push(`<meta name="twitter:title" content="${title}">`);
|
||||||
|
|
||||||
return metadataElements.join('\n');
|
return metadataElements.join('\n');
|
||||||
}
|
}
|
||||||
|
BIN
static/img/icon-grist.png
Normal file
BIN
static/img/icon-grist.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 9.9 KiB |
@ -1,6 +1,8 @@
|
|||||||
{
|
{
|
||||||
"sendAppPage": {
|
"sendAppPage": {
|
||||||
"Loading": "Loading"
|
"Loading": "Loading",
|
||||||
|
"gristMetaDescription": "Grist is the evolution of spreadsheets.",
|
||||||
|
"Welcome": "Welcome"
|
||||||
},
|
},
|
||||||
"oidc": {
|
"oidc": {
|
||||||
"emailNotVerifiedError": "Please verify your email with the identity provider, and log in again."
|
"emailNotVerifiedError": "Please verify your email with the identity provider, and log in again."
|
||||||
|
@ -29,6 +29,7 @@ describe('HomeIntro', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should should intro screen for anon', () => testIntroScreen({anon: true, team: false}));
|
it('should should intro screen for anon', () => testIntroScreen({anon: true, team: false}));
|
||||||
|
it('should should set meta tags for URL previews', testMetaTags);
|
||||||
it('should not show Other Sites section', testOtherSitesSection);
|
it('should not show Other Sites section', testOtherSitesSection);
|
||||||
it('should allow create/import from intro screen', testCreateImport.bind(null, false));
|
it('should allow create/import from intro screen', testCreateImport.bind(null, false));
|
||||||
it('should link to examples page from the intro', testExamplesPage);
|
it('should link to examples page from the intro', testExamplesPage);
|
||||||
@ -129,6 +130,22 @@ describe('HomeIntro', function() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function testMetaTags() {
|
||||||
|
const expectedTitle = 'Welcome - Grist';
|
||||||
|
assert.equal(await driver.find('meta[name="twitter:title"]').getAttribute('content'), expectedTitle);
|
||||||
|
assert.equal(await driver.find('meta[property="og:title"]').getAttribute('content'), expectedTitle);
|
||||||
|
|
||||||
|
const expectedDescription = 'Grist is the evolution of spreadsheets.';
|
||||||
|
assert.equal(await driver.find('meta[name="description"]').getAttribute('content'), expectedDescription);
|
||||||
|
assert.equal(await driver.find('meta[name="twitter:description"]').getAttribute('content'), expectedDescription);
|
||||||
|
assert.equal(await driver.find('meta[property="og:description"]').getAttribute('content'), expectedDescription);
|
||||||
|
|
||||||
|
const gristIconFileName = 'icon-grist.png';
|
||||||
|
assert.include(await driver.find('meta[name="thumbnail"]').getAttribute('content'), gristIconFileName);
|
||||||
|
assert.include(await driver.find('meta[name="twitter:image"]').getAttribute('content'), gristIconFileName);
|
||||||
|
assert.include(await driver.find('meta[property="og:image"]').getAttribute('content'), gristIconFileName);
|
||||||
|
}
|
||||||
|
|
||||||
async function testCreateImport(isLoggedIn: boolean) {
|
async function testCreateImport(isLoggedIn: boolean) {
|
||||||
await checkIntroButtons(isLoggedIn);
|
await checkIntroButtons(isLoggedIn);
|
||||||
|
|
||||||
|
@ -18,7 +18,22 @@ describe('NewDocument.ntest', function() {
|
|||||||
this.timeout(10000);
|
this.timeout(10000);
|
||||||
await gu.actions.createNewDoc('Untitled');
|
await gu.actions.createNewDoc('Untitled');
|
||||||
assert.equal(await gu.actions.getDocTitle(), 'Untitled');
|
assert.equal(await gu.actions.getDocTitle(), 'Untitled');
|
||||||
assert.equal(await driver.getTitle(), 'Untitled - Grist');
|
|
||||||
|
const expectedTitle = 'Untitled - Grist';
|
||||||
|
assert.equal(await driver.getTitle(), expectedTitle);
|
||||||
|
assert.equal(await driver.find('meta[name="twitter:title"]').getAttribute('content'), expectedTitle);
|
||||||
|
assert.equal(await driver.find('meta[property="og:title"]').getAttribute('content'), expectedTitle);
|
||||||
|
|
||||||
|
const expectedDescription = 'Grist is the evolution of spreadsheets.';
|
||||||
|
assert.equal(await driver.find('meta[name="description"]').getAttribute('content'), expectedDescription);
|
||||||
|
assert.equal(await driver.find('meta[name="twitter:description"]').getAttribute('content'), expectedDescription);
|
||||||
|
assert.equal(await driver.find('meta[property="og:description"]').getAttribute('content'), expectedDescription);
|
||||||
|
|
||||||
|
const gristIconFileName = 'icon-grist.png';
|
||||||
|
assert.include(await driver.find('meta[name="thumbnail"]').getAttribute('content'), gristIconFileName);
|
||||||
|
assert.include(await driver.find('meta[name="twitter:image"]').getAttribute('content'), gristIconFileName);
|
||||||
|
assert.include(await driver.find('meta[property="og:image"]').getAttribute('content'), gristIconFileName);
|
||||||
|
|
||||||
assert.equal(await $('.active_section .test-viewsection-title').wait().text(), 'TABLE1');
|
assert.equal(await $('.active_section .test-viewsection-title').wait().text(), 'TABLE1');
|
||||||
await gu.waitForServer();
|
await gu.waitForServer();
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user