Skip to content

Commit

Permalink
[lightbox-media-viewer]: Auto-open and play media from targeted link (c…
Browse files Browse the repository at this point in the history
…arbon-design-system#11253)

### Related Ticket(s)

Closes carbon-design-system#11213
[Jira ticket](https://jsw.ibm.com/browse/ADCMS-4360)

### Description

Enables the `CTAMixin` to check the URL fragment for a particular pattern, `cta-video-[video-id]`, and if present, kick off the CTA trigger, which in the case of a video CTA, is opening the corresponding lightbox and auto-playing video. The effect is that visiting any page with a video CTA for a specific video embedded in it (eg. `0_ibuqxqbe`), with a URL fragment like `#cta-video-0_ibuqxqbe`, will automatically open the lightbox modal and play the video (provided [browser auto-play policies to not block it](https://developer.chrome.com/blog/autoplay/))

### Testing instructions

 - [ ] Navigate to any page with a CTA component that allows you to configure it as a video component. Examples include: [Link with icon](https://ibmdotcom-webcomponents.s3.us-east.cloud-object-storage.appdomain.cloud/deploy-previews/11253/index.html?path=/story/components-link-with-icon--default)
 - [ ] Set the CTA type (cta-type) to Video (video)
 - [ ] Take note of the configured Video ID (eg. `0_ibuqxqbe`). 
 - [ ] Click Open canvas in new tab
 - [ ] The component should work without any regressions
 - [ ] Append a URL fragment to the URL like `#cta-video-[video_id]`
 - [ ] When loading the page with the URL fragment, the lightbox should automatically open and begin playback of the video (subject to [browser auto-play policies](https://developer.chrome.com/blog/autoplay/)). Note that mosy likely, the video _won't_ autoplay b/c you probably haven't built up enough of Media Engagement Index (Chrome speak) against the Storybook deploy preview URL. We don't have control over this.


### Changelog

**New**

- `CTAMixin`, where `ctaType === CTA_TYPE.VIDEO`,  will check for a specifically crafted URL fragment (`cta-video-[video-id]`) to trigger it's run action.

<!-- React and Web Component deploy previews are enabled by default. -->
<!-- To enable additional available deploy previews, apply the following -->
<!-- labels for the corresponding package: -->
<!-- *** "test: e2e": Codesandbox examples and e2e integration tests -->
<!-- *** "package: services": Services -->
<!-- *** "package: utilities": Utilities -->
<!-- *** "RTL": React / Web Components (RTL) -->
<!-- *** "feature flag": React / Web Components (experimental) -->
  • Loading branch information
m4olivei authored and kennylam committed Mar 11, 2024
1 parent c0cac3f commit af6d503
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 15 deletions.
6 changes: 6 additions & 0 deletions packages/web-components/IMPLEMENTATION_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,12 @@ There are some common behaviors in CTA components that are implemented by
attribute of `<a>` for `external` CTA types.
- [Use a hash link](https://github.com/carbon-design-system/carbon-for-ibm-dotcom/blob/v1.15.0-rc.0/packages/web-components/src/component-mixins/cta/cta.ts#L113-L122)
for `video` CTA types.
- Trigger a CTA video on page load using a `#cta-video-[video-id]` URL fragment.
For any page that includes a CTA component composed with `CTAMixin`, the CTA
will look for a URL fragment following the pattern `#cta-video-[video-id]`,
where `[video-id]` is the video id configured for the component. If there is a
match, the CTA will automatically be triggered, which in most cases will open
a lightbox and begin video playback (subject to browser auto-play policies).

### Video CTA

Expand Down
51 changes: 50 additions & 1 deletion packages/web-components/src/component-mixins/cta/cta.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* @license
*
* Copyright IBM Corp. 2020, 2023
* Copyright IBM Corp. 2020, 2024
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
Expand Down Expand Up @@ -29,6 +29,7 @@ import {
formatVideoCaption,
formatVideoDuration,
} from '../../internal/vendor/@carbon/ibmdotcom-utilities/utilities/formatVideoCaption/formatVideoCaption.js';
import root from 'window-or-global';

const { prefix, stablePrefix: c4dPrefix } = settings;

Expand Down Expand Up @@ -201,6 +202,14 @@ const CTAMixin = <T extends Constructor<HTMLElement>>(Base: T) => {
`;
}

firstUpdated() {
const { ctaType, href } = this;
// Check for the URL trigger meant to fire eventRunAction.
if (ctaType === CTA_TYPE.VIDEO && href) {
this._checkUrlVideoTrigger();
}
}

/**
* Handles `.updated()` method of `lit-element`.
*/
Expand Down Expand Up @@ -324,6 +333,46 @@ const CTAMixin = <T extends Constructor<HTMLElement>>(Base: T) => {
}
}

/**
* Check the URL for a fragment including the video id.
*
* If we find a URL fragment that includes the video id, we trigger the
* eventRunAction event, which for video will open the video and start
* playback in a lightbox. This is the same thing that happens when the user
* clicks on the CTA.
*/
_checkUrlVideoTrigger() {
const { ctaType, disabled, href, videoDescription, videoName } = this;
// Without a video id, or if the button is disabled, there is nothing to
// do here.
if (ctaType !== CTA_TYPE.VIDEO || !href || disabled) {
return;
}
// Only trigger for the first CTA with the video id in the page.
if (this.ownerDocument.querySelector(`[href='${href}']`) !== this) {
return;
}
const { eventRunAction } = this.constructor as typeof CTAMixinImpl;
const hash = root.location.hash;
const urlTrigger = `cta-video-${href}`;

if (hash === `#${urlTrigger}`) {
this.dispatchEvent(
new CustomEvent(eventRunAction, {
bubbles: true,
cancelable: true,
composed: true,
detail: {
href,
ctaType,
videoName,
videoDescription,
},
})
);
}
}

/**
* Updates video thumbnail url to match card width.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* @license
*
* Copyright IBM Corp. 2020, 2023
* Copyright IBM Corp. 2020, 2024
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
Expand Down Expand Up @@ -530,7 +530,7 @@ class C4DExpressiveModal extends StableSelectorMixin(
static get selectorPrimaryFocus() {
return `
[data-modal-primary-focus],
${c4dPrefix}-expressive-modal-footer ${c4dPrefix}-button[kind="primary"],
${c4dPrefix}-expressive-modal-footer ${c4dPrefix}-button[kind="primary"]
`;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright IBM Corp. 2021, 2022
* Copyright IBM Corp. 2021, 2023
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
Expand All @@ -15,13 +15,19 @@
*/
const _path = '/iframe.html?id=components-link-with-icon--default';

const _videoId = '0_ibuqxqbe';

const _videoPath = `/iframe.html?&id=components-link-with-icon--default&knob-CTA%20type%20(cta-type)=video&knob-Icon%20Position%20(icon-placement):=right&knob-Video%20ID%20(href)=${_videoId}`;

/**
* Defines the component selector.
*
* @type {string}
* @private
*/
const _selector = '[data-autoid="cds--link-with-icon"]';
const _selector = '[data-autoid="c4d--link-with-icon"]';

const _lightboxVideoPlayerSelector = 'c4d-lightbox-video-player';

/**
* Collection of test scenarios.
Expand All @@ -47,7 +53,9 @@ const _tests = [
.then(([copy]) => {
defaultCopy = copy.innerText.trim();
})
.visit(`${_path}&knob-Link%20text%20(unnamed%20slot)=${customCopyInput}`)
.visit(
`${_path}&knob-Link%20text%20(unnamed%20slot)=${customCopyInput}`
)
.get(_selector)
.should(([copy]) => {
customCopyOutput = copy.innerText.trim();
Expand All @@ -66,16 +74,18 @@ const _tests = [
.get(_selector)
.shadow()
.find('a')
.should($link => {
.should(($link) => {
defaultHref = $link.prop('href');

expect($link.prop('href')).not.to.be.empty;
})
.visit(`${_path}&knob-Link%20href%20(href)=${customHrefInput}`)
.visit(
`${_path}&knob-Content%20link%20href%20(href)=${customHrefInput}`
)
.get(_selector)
.shadow()
.find('a')
.should($link => {
.should(($link) => {
customHrefOutput = $link.prop('href');

expect(customHrefOutput).to.be.eq(customHrefInput);
Expand All @@ -94,16 +104,18 @@ const _tests = [
},
() => {
it('should check icon placements', () => {
['left', 'right'].forEach(placement => {
['left', 'right'].forEach((placement) => {
let $svg;
cy.visit(`${_path}&knob-Icon%20Position%20(icon-placement):=${placement}`)
cy.visit(
`${_path}&knob-Icon%20Position%20(icon-placement):=${placement}`
)
.get(_selector)
.then($elem => {
.then(($elem) => {
$svg = $elem.find('svg');
})
.shadow()
.find('a')
.should($link => {
.should(($link) => {
const svgPosition = $svg[0].getBoundingClientRect();
const textPosition = $link.find('span')[0].getBoundingClientRect();

Expand All @@ -123,20 +135,35 @@ const _tests = [
});
});
},
() => {
it('should replace the button title with the video title for a video cta type', () => {
cy.visit(_videoPath);
cy.get(_selector)
.shadow()
.find('a > span')
.should('contain.text', 'Mombo');
cy.get(_lightboxVideoPlayerSelector).should('not.exist');
});

it('should trigger the lightbox for video when using the right URL fragment', () => {
cy.visit(`${_videoPath}#cta-video-${_videoId}`);
cy.get(_lightboxVideoPlayerSelector).should('exist').and('be.visible');
});
},
];

describe('cds-link-with-icon | default (desktop)', () => {
beforeEach(() => {
cy.viewport(1280, 780);
});

_tests.forEach(test => test());
_tests.forEach((test) => test());
});

describe('cds-link-with-icon | default (mobile)', () => {
beforeEach(() => {
cy.viewport(375, 720);
});

_tests.forEach(test => test());
_tests.forEach((test) => test());
});

0 comments on commit af6d503

Please sign in to comment.