From 240bb5bd68dc60162211630769cc247461a9e5d7 Mon Sep 17 00:00:00 2001 From: rayangler <27821750+rayangler@users.noreply.github.com> Date: Fri, 27 Sep 2024 08:24:29 -0400 Subject: [PATCH 01/14] DOP-4916: Implement SoftwareSourceCode structured data --- src/components/Code/Code.js | 139 +++++++++++++++++++---------------- src/utils/get-language.js | 49 ++++++++++++ src/utils/structured-data.js | 63 ++++++++++++++++ 3 files changed, 188 insertions(+), 63 deletions(-) create mode 100644 src/utils/structured-data.js diff --git a/src/components/Code/Code.js b/src/components/Code/Code.js index e5d5a080f..9a969b044 100644 --- a/src/components/Code/Code.js +++ b/src/components/Code/Code.js @@ -1,5 +1,5 @@ import { css } from '@emotion/react'; -import React, { useCallback, useContext } from 'react'; +import React, { useCallback, useContext, useMemo } from 'react'; import PropTypes from 'prop-types'; import styled from '@emotion/styled'; import { default as CodeBlock } from '@leafygreen-ui/code'; @@ -12,6 +12,7 @@ import { TabContext } from '../Tabs/tab-context'; import { reportAnalytics } from '../../utils/report-analytics'; import { getLanguage } from '../../utils/get-language'; import { DRIVER_ICON_MAP } from '../icons/DriverIconMap'; +import { SoftwareSourceCodeSd } from '../../utils/structured-data'; import { baseCodeStyle, borderCodeStyle, lgStyles } from './styles/codeStyle'; import { CodeContext } from './code-context'; @@ -85,77 +86,89 @@ const Code = ({ reportAnalytics('CodeblockCopied', { code }); }, [code]); + const softwareSourceCodeSd = useMemo(() => new SoftwareSourceCodeSd({ code, lang }), [code, lang]); + return ( -
div > div { - display: grid; - grid-template-columns: ${!copyable && (languageOptions?.length === 0 || language === 'none') - ? 'auto 0px !important' - : 'code panel'}; - } - - > div { - border-top-left-radius: ${captionBorderRadius}; - border-top-right-radius: ${captionBorderRadius}; - display: grid; - border-color: ${palette.gray.light2}; - - .dark-theme & { - border-color: ${palette.gray.dark2}; + <> + {softwareSourceCodeSd.isValid() && ( + .emotion-0 { display: table; margin: 24px 0; @@ -402,6 +407,11 @@ exports[`renders correctly 1`] = ` exports[`renders correctly when none is passed in as a language 1`] = ` + .emotion-0 { display: table; margin: 24px 0; diff --git a/tests/unit/__snapshots__/CodeIO.test.js.snap b/tests/unit/__snapshots__/CodeIO.test.js.snap index 983f5b7e4..fb00c0bed 100644 --- a/tests/unit/__snapshots__/CodeIO.test.js.snap +++ b/tests/unit/__snapshots__/CodeIO.test.js.snap @@ -657,6 +657,11 @@ exports[`CodeIO renders correctly 1`] = `
+
diff --git a/tests/unit/__snapshots__/Collapsible.test.js.snap b/tests/unit/__snapshots__/Collapsible.test.js.snap index 6a068d4b6..8cfe60d3b 100644 --- a/tests/unit/__snapshots__/Collapsible.test.js.snap +++ b/tests/unit/__snapshots__/Collapsible.test.js.snap @@ -586,6 +586,11 @@ exports[`collapsible component renders all the content in the options and childr > This is collapsible content

+
diff --git a/tests/unit/__snapshots__/LiteralInclude.test.js.snap b/tests/unit/__snapshots__/LiteralInclude.test.js.snap index 3fb0fb180..f55e71ab2 100644 --- a/tests/unit/__snapshots__/LiteralInclude.test.js.snap +++ b/tests/unit/__snapshots__/LiteralInclude.test.js.snap @@ -2,6 +2,11 @@ exports[`renders correctly 1`] = ` + .emotion-0 { display: table; margin: 24px 0; diff --git a/tests/unit/__snapshots__/ReleaseSpecification.test.js.snap b/tests/unit/__snapshots__/ReleaseSpecification.test.js.snap index 2731a55ee..f812ed02a 100644 --- a/tests/unit/__snapshots__/ReleaseSpecification.test.js.snap +++ b/tests/unit/__snapshots__/ReleaseSpecification.test.js.snap @@ -2,6 +2,11 @@ exports[`renders correctly 1`] = ` + .emotion-0 { display: table; margin: 24px 0; diff --git a/tests/unit/utils/__snapshots__/structured-data.test.js.snap b/tests/unit/utils/__snapshots__/structured-data.test.js.snap new file mode 100644 index 000000000..9c2791b69 --- /dev/null +++ b/tests/unit/utils/__snapshots__/structured-data.test.js.snap @@ -0,0 +1,19 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`SoftwareSourceCode returns valid structured data with programmingLanguage 1`] = ` +SoftwareSourceCodeSd { + "@context": "https://schema.org", + "@type": "SoftwareSourceCode", + "codeSampleType": "code snippet", + "text": "print("hello world")", +} +`; + +exports[`SoftwareSourceCode returns valid structured data without programmingLangauge 1`] = ` +SoftwareSourceCodeSd { + "@context": "https://schema.org", + "@type": "SoftwareSourceCode", + "codeSampleType": "code snippet", + "text": "print("hello world")", +} +`; diff --git a/tests/unit/utils/structured-data.test.js b/tests/unit/utils/structured-data.test.js new file mode 100644 index 000000000..8f7e30ed9 --- /dev/null +++ b/tests/unit/utils/structured-data.test.js @@ -0,0 +1,16 @@ +import { SoftwareSourceCodeSd } from '../../../src/utils/structured-data'; + +describe('SoftwareSourceCode', () => { + it('returns valid structured data with programmingLanguage', () => { + const code = 'print("hello world")'; + const lang = 'py'; + const softwareSourceCodeSd = new SoftwareSourceCodeSd({ code, lang }); + expect(softwareSourceCodeSd).toMatchSnapshot(); + }); + + it('returns valid structured data without programmingLangauge', () => { + const code = 'print("hello world")'; + const softwareSourceCodeSd = new SoftwareSourceCodeSd({ code }); + expect(softwareSourceCodeSd).toMatchSnapshot(); + }); +}); From 73991da9c00826551f29eb86754e7c39d7163f58 Mon Sep 17 00:00:00 2001 From: rayangler <27821750+rayangler@users.noreply.github.com> Date: Fri, 27 Sep 2024 09:24:31 -0400 Subject: [PATCH 03/14] Update VideObject structured data --- src/components/Video/index.js | 33 ++++++++----------- src/utils/structured-data.js | 20 +++++++++++ .../structured-data.test.js.snap | 23 +++++++++++++ tests/unit/utils/structured-data.test.js | 29 +++++++++++++++- 4 files changed, 84 insertions(+), 21 deletions(-) diff --git a/src/components/Video/index.js b/src/components/Video/index.js index 922252fb6..430ee4cc1 100644 --- a/src/components/Video/index.js +++ b/src/components/Video/index.js @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect, useMemo } from 'react'; import ReactPlayerYT from 'react-player/youtube'; import ReactPlayerWistia from 'react-player/wistia'; import PropTypes from 'prop-types'; @@ -7,6 +7,7 @@ import { css } from '@emotion/react'; import { palette } from '@leafygreen-ui/palette'; import { withPrefix } from 'gatsby'; import { theme } from '../../theme/docsTheme'; +import { VideoObjectSd } from '../../utils/structured-data'; import VideoPlayButton from './VideoPlayButton'; // Imported both players to keep bundle size low and rendering the one associated to the URL being passed in @@ -73,21 +74,10 @@ const Video = ({ nodeData: { argument, options = {} } }) => { // use placeholder image for video thumbnail if invalid URL provided const [previewImage, setPreviewImage] = useState(withPrefix('assets/meta_generic.png')); const { title, description, 'upload-date': uploadDate, 'thumbnail-url': thumbnailUrl } = options; - // Required fields based on https://developers.google.com/search/docs/appearance/structured-data/video#video-object - const hasAllReqFields = [url, title, uploadDate, thumbnailUrl].every((val) => !!val); - - const structuredData = { - '@context': 'https://schema.org', - '@type': 'VideoObject', - embedUrl: url, - name: title, - uploadDate, - thumbnailUrl, - }; - - if (description) { - structuredData['description'] = description; - } + const videoObjectSd = useMemo( + () => new VideoObjectSd({ embedUrl: url, name: title, uploadDate, thumbnailUrl, description }), + [url, title, uploadDate, thumbnailUrl, description] + ); useEffect(() => { // handles URL validity checking for well-formed YT links @@ -121,10 +111,13 @@ const Video = ({ nodeData: { argument, options = {} } }) => { return ( <> - {hasAllReqFields && ( - + {videoObjectSd.isValid() && ( +
-
-            
-              
-                
-                  
-                    
+                    
-                      1
-                    
-                    
-                  
-                
-              
+
- hello world -
-
-
+ + 1 + + + hello world + + + + + + +
From 4d087d43e373c1b799a3b7c3953eab64d989f636 Mon Sep 17 00:00:00 2001 From: rayangler <27821750+rayangler@users.noreply.github.com> Date: Fri, 27 Sep 2024 14:22:32 -0400 Subject: [PATCH 09/14] Validate as part of memo --- src/components/Code/Code.js | 7 +++++-- src/components/Code/Output.js | 7 +++++-- src/components/Video/index.js | 10 +++++----- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/components/Code/Code.js b/src/components/Code/Code.js index 9a969b044..b151e5b9f 100644 --- a/src/components/Code/Code.js +++ b/src/components/Code/Code.js @@ -86,11 +86,14 @@ const Code = ({ reportAnalytics('CodeblockCopied', { code }); }, [code]); - const softwareSourceCodeSd = useMemo(() => new SoftwareSourceCodeSd({ code, lang }), [code, lang]); + const softwareSourceCodeSd = useMemo(() => { + const sd = new SoftwareSourceCodeSd({ code, lang }); + return sd.isValid() ? sd.toString() : undefined; + }, [code, lang]); return ( <> - {softwareSourceCodeSd.isValid() && ( + {softwareSourceCodeSd && (