diff --git a/.changeset/thirty-forks-share.md b/.changeset/thirty-forks-share.md new file mode 100644 index 0000000000..15c5f2a80b --- /dev/null +++ b/.changeset/thirty-forks-share.md @@ -0,0 +1,5 @@ +--- +"@tiptap/extension-youtube": patch +--- + +This change adds option to set rel parameter on youtube embed link diff --git a/demos/src/Nodes/Youtube/React/index.spec.js b/demos/src/Nodes/Youtube/React/index.spec.js index b2424d0f43..d928621cc0 100644 --- a/demos/src/Nodes/Youtube/React/index.spec.js +++ b/demos/src/Nodes/Youtube/React/index.spec.js @@ -13,7 +13,15 @@ context('/src/Nodes/Youtube/React/', () => { cy.get('#add').eq(0).click() cy.get('.tiptap div[data-youtube-video] iframe') .should('have.length', 1) - .should('have.attr', 'src', 'https://www.youtube-nocookie.com/embed/hBp4dgE7Bho?controls=0') + .invoke('attr', 'src') + .then(src => { + const url = new URL(src) + + expect(`${url.origin}${url.pathname}`).to.eq('https://www.youtube-nocookie.com/embed/hBp4dgE7Bho') + expect([...url.searchParams.keys()]).to.have.members(['controls', 'rel']) + expect(url.searchParams.get('controls')).to.eq('0') + expect(url.searchParams.get('rel')).to.eq('1') + }) }) }) @@ -24,9 +32,17 @@ context('/src/Nodes/Youtube/React/', () => { cy.get('#height').type('{selectall}{backspace}240') cy.get('#add').eq(0).click() cy.get('.tiptap div[data-youtube-video] iframe').should('have.length', 1) - .should('have.attr', 'src', 'https://www.youtube-nocookie.com/embed/hBp4dgE7Bho?controls=0') .should('have.css', 'width', '320px') .should('have.css', 'height', '240px') + .invoke('attr', 'src') + .then(src => { + const url = new URL(src) + + expect(`${url.origin}${url.pathname}`).to.eq('https://www.youtube-nocookie.com/embed/hBp4dgE7Bho') + expect([...url.searchParams.keys()]).to.have.members(['controls', 'rel']) + expect(url.searchParams.get('controls')).to.eq('0') + expect(url.searchParams.get('rel')).to.eq('1') + }) }) }) @@ -45,7 +61,15 @@ context('/src/Nodes/Youtube/React/', () => { cy.get('#add').eq(0).click() cy.get('.tiptap div[data-youtube-video] iframe') .should('have.length', 1) - .should('have.attr', 'src', 'https://www.youtube-nocookie.com/embed/hBp4dgE7Bho?controls=0') + .invoke('attr', 'src') + .then(src => { + const url = new URL(src) + + expect(`${url.origin}${url.pathname}`).to.eq('https://www.youtube-nocookie.com/embed/hBp4dgE7Bho') + expect([...url.searchParams.keys()]).to.have.members(['controls', 'rel']) + expect(url.searchParams.get('controls')).to.eq('0') + expect(url.searchParams.get('rel')).to.eq('1') + }) cy.get('.tiptap div[data-youtube-video] iframe') .click() @@ -54,7 +78,15 @@ context('/src/Nodes/Youtube/React/', () => { cy.get('.tiptap div[data-youtube-video] iframe') .should('have.length', 1) - .should('have.attr', 'src', 'https://www.youtube-nocookie.com/embed/wRakoMYVHm8?controls=0') + .invoke('attr', 'src') + .then(src => { + const url = new URL(src) + + expect(`${url.origin}${url.pathname}`).to.eq('https://www.youtube-nocookie.com/embed/wRakoMYVHm8') + expect([...url.searchParams.keys()]).to.have.members(['controls', 'rel']) + expect(url.searchParams.get('controls')).to.eq('0') + expect(url.searchParams.get('rel')).to.eq('1') + }) }) }) }) diff --git a/demos/src/Nodes/Youtube/Vue/index.spec.js b/demos/src/Nodes/Youtube/Vue/index.spec.js index 79dddd4e68..af6b2fedaf 100644 --- a/demos/src/Nodes/Youtube/Vue/index.spec.js +++ b/demos/src/Nodes/Youtube/Vue/index.spec.js @@ -13,7 +13,15 @@ context('/src/Nodes/Youtube/Vue/', () => { cy.get('#add').eq(0).click() cy.get('.tiptap div[data-youtube-video] iframe') .should('have.length', 1) - .should('have.attr', 'src', 'https://www.youtube-nocookie.com/embed/hBp4dgE7Bho?controls=0') + .invoke('attr', 'src') + .then(src => { + const url = new URL(src) + + expect(`${url.origin}${url.pathname}`).to.eq('https://www.youtube-nocookie.com/embed/hBp4dgE7Bho') + expect([...url.searchParams.keys()]).to.have.members(['controls', 'rel']) + expect(url.searchParams.get('controls')).to.eq('0') + expect(url.searchParams.get('rel')).to.eq('1') + }) }) }) @@ -24,9 +32,17 @@ context('/src/Nodes/Youtube/Vue/', () => { cy.get('#height').type('{selectall}{backspace}240') cy.get('#add').eq(0).click() cy.get('.tiptap div[data-youtube-video] iframe').should('have.length', 1) - .should('have.attr', 'src', 'https://www.youtube-nocookie.com/embed/hBp4dgE7Bho?controls=0') .should('have.css', 'width', '320px') .should('have.css', 'height', '240px') + .invoke('attr', 'src') + .then(src => { + const url = new URL(src) + + expect(`${url.origin}${url.pathname}`).to.eq('https://www.youtube-nocookie.com/embed/hBp4dgE7Bho') + expect([...url.searchParams.keys()]).to.have.members(['controls', 'rel']) + expect(url.searchParams.get('controls')).to.eq('0') + expect(url.searchParams.get('rel')).to.eq('1') + }) }) }) @@ -45,7 +61,15 @@ context('/src/Nodes/Youtube/Vue/', () => { cy.get('#add').eq(0).click() cy.get('.tiptap div[data-youtube-video] iframe') .should('have.length', 1) - .should('have.attr', 'src', 'https://www.youtube-nocookie.com/embed/hBp4dgE7Bho?controls=0') + .invoke('attr', 'src') + .then(src => { + const url = new URL(src) + + expect(`${url.origin}${url.pathname}`).to.eq('https://www.youtube-nocookie.com/embed/hBp4dgE7Bho') + expect([...url.searchParams.keys()]).to.have.members(['controls', 'rel']) + expect(url.searchParams.get('controls')).to.eq('0') + expect(url.searchParams.get('rel')).to.eq('1') + }) cy.get('.tiptap div[data-youtube-video] iframe') .click() @@ -54,7 +78,15 @@ context('/src/Nodes/Youtube/Vue/', () => { cy.get('.tiptap div[data-youtube-video] iframe') .should('have.length', 1) - .should('have.attr', 'src', 'https://www.youtube-nocookie.com/embed/wRakoMYVHm8?controls=0') + .invoke('attr', 'src') + .then(src => { + const url = new URL(src) + + expect(`${url.origin}${url.pathname}`).to.eq('https://www.youtube-nocookie.com/embed/wRakoMYVHm8') + expect([...url.searchParams.keys()]).to.have.members(['controls', 'rel']) + expect(url.searchParams.get('controls')).to.eq('0') + expect(url.searchParams.get('rel')).to.eq('1') + }) }) }) }) diff --git a/packages/extension-youtube/src/utils.ts b/packages/extension-youtube/src/utils.ts index 5f1137876e..ae8e217522 100644 --- a/packages/extension-youtube/src/utils.ts +++ b/packages/extension-youtube/src/utils.ts @@ -24,6 +24,7 @@ export interface GetEmbedUrlOptions { playlist?: string; progressBarColor?: string; startAt?: number; + rel?: number; } export const getYoutubeEmbedUrl = (nocookie?: boolean) => { @@ -50,6 +51,7 @@ export const getEmbedUrlFromYoutubeUrl = (options: GetEmbedUrlOptions) => { playlist, progressBarColor, startAt, + rel, } = options if (!isValidYoutubeUrl(url)) { @@ -146,6 +148,10 @@ export const getEmbedUrlFromYoutubeUrl = (options: GetEmbedUrlOptions) => { params.push(`color=${progressBarColor}`) } + if (rel !== undefined) { + params.push(`rel=${rel}`) + } + if (params.length) { outputUrl += `?${params.join('&')}` } diff --git a/packages/extension-youtube/src/youtube.ts b/packages/extension-youtube/src/youtube.ts index 3ddc279ed9..177af8c7ad 100644 --- a/packages/extension-youtube/src/youtube.ts +++ b/packages/extension-youtube/src/youtube.ts @@ -149,6 +149,13 @@ export interface YoutubeOptions { * @example 1280 */ width: number; + + /** + * Controls if the related youtube videos at the end are from the same channel. + * @default 1 + * @example 0 + */ + rel: number; } /** @@ -199,6 +206,7 @@ export const Youtube = Node.create({ playlist: '', progressBarColor: undefined, width: 640, + rel: 1, } }, @@ -288,6 +296,7 @@ export const Youtube = Node.create({ playlist: this.options.playlist, progressBarColor: this.options.progressBarColor, startAt: HTMLAttributes.start || 0, + rel: this.options.rel, }) HTMLAttributes.src = embedUrl @@ -316,6 +325,7 @@ export const Youtube = Node.create({ origin: this.options.origin, playlist: this.options.playlist, progressBarColor: this.options.progressBarColor, + rel: this.options.rel, }, HTMLAttributes, ),