Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

35102/Banner Web Component #284

Merged
merged 35 commits into from
Mar 1, 2022
Merged

35102/Banner Web Component #284

merged 35 commits into from
Mar 1, 2022

Conversation

pdavies88
Copy link
Contributor

@pdavies88 pdavies88 commented Feb 9, 2022

Description

Closes department-of-veterans-affairs/va.gov-team#35102

Testing done

Screen Shot 2022-02-11 at 7 57 46 AM

Screen Shot 2022-02-11 at 7 46 29 AM

Screen Shot 2022-02-11 at 7 55 24 AM

Screenshots

Screen Shot 2022-02-10 at 3 36 04 PM

Screen Shot 2022-02-10 at 3 36 31 PM

Screen Shot 2022-02-10 at 3 37 00 PM

Acceptance criteria

  • va-banner is written
  • E2E tests are written
  • Component is checked for accessibility issues
  • Alert Carol that component is ready for staging review
  • Address any staging review comments
  • Merge component

Definition of done

  • Documentation has been updated, if applicable
  • A link has been provided to the originating GitHub issue (or connected to it via ZenHub)

@pdavies88 pdavies88 added the major Major change in semantic versioning label Feb 10, 2022
@pdavies88 pdavies88 added minor For a minor Semantic Version change and removed major Major change in semantic versioning labels Feb 10, 2022
@pdavies88 pdavies88 changed the title 35102/banner web component 35102/Banner Web Component Feb 10, 2022
@pdavies88 pdavies88 marked this pull request as ready for review February 10, 2022 20:39
@pdavies88 pdavies88 requested a review from a team as a code owner February 10, 2022 20:39

// This querySelector finds the Mock Element but it is not a E2EEElement
// E2EEElements seem to be reserved to only the hosts shadowroot elements like va-alert
const button = await vaAlert.shadowRoot.querySelector('button');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about const button = await page.find('pierce/button') ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I meant to comment not request changes. Sorry!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you this was exactly what I was looking for to get the test working!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is that pierce/button a puppeteer thing or a stencil thing?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's from pupeeter. We're also using this in tests for va-additional-info and va-select.


expect(Array.from(vaBanner.shadowRoot.childNodes)).toHaveLength(0);

await page.evaluate(() => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Testing a page refresh to ensure that the banner is still dismissed

// Track the dismiss event in Google Analytics
if (!this.disableAnalytics) {
const detail = {
componentName: 'Banner',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know some of the early web components used their React component names for this, but going forward we should probably use the web component name. Correct me if I'm wrong @bsmartin-ep

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are correct @bkjohnson. We have been matching the web component name.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bsmartin-ep thank you I will update the component name. However a couple of questions:

  1. What would you like the details of the Analytics event to be for dismiss? Right now it is clickLabel: "Dismiss Banner", headline: string, showClose: boolean, windowSession: boolean, type: string
  2. We have a va-alert component nested inside our va-banner component. Therefore the analytics event for the va-alert that is enabled for when a link is clicked will fire inside of the va-banner component. Do we need a second analytics event that would be exclusive to the va-banner for when a link is clicked?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pdavies88 -

Apologies - missed these questions earlier.

  1. This is event int-alert-box-close, correct? We're only looking for alert-box-headline in the dataLayer. Which got me thinking this setup file isn't working correctly. We just kebab case the component name and prepend it to the dataLayer key. Which won't work for us here, since we'll get va-alert-headline. Want me to send in a PR allowing us to set an analytics prefix in that setup file?
  2. No - unless there are new event parameters we need to add, I think deferring to the va-alert event sounds good to me.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bsmartin-ep thanks for the info yes the original event is int-alert-box-close.

So correct me if I am wrong but I believe the event would become va-banner-alert-box-headline

const detail = {
componentName: 'va-banner',
action: 'click',
details: {
'alert-box-headline': this.headline,
},
};

and then in https://github.com/department-of-veterans-affairs/vets-website/blob/9e5c8399e9dbe100a88ee22899ed9d94070538c3/src/platform/site-wide/component-library-analytics-setup.js#L9-L35

I would need to add in:
'va-banner': [{ action: 'click', event: 'int-alert-box-close' }],
and
'va-alert': [{ action: 'linkClick', event: 'nav-warning-alert-box-content-link-click' }],
since they don't exist there yet

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So correct me if I am wrong but I believe the event would become va-banner-alert-box-headline

@pdavies88 -

Unfortunately I don't think this will work for us. Our tagging system gives us some flexibility with regex-ing event names but not parameters. We would have to create a mess of fall-through default values in GTM.

I think our best bet is to move away from the kebab-casing of the component name and add in the GA event parameter prefix we want to the lookup table:

'va-alert': [
     { action: 'linkClick', parameter_prefix: 'alert-box', event: 'nav-warning-alert-box-content-link-click' }
],

...or maybe slightly restructuring the array into an object...

'va-alert': {
     parameter_prefix: 'alert-box',
     events: [{ action: 'linkClick', , event: 'nav-warning-alert-box-content-link-click' }]
},

Happy to send in a PR. Or fix this in a separate concurrent issue (since your changes didn't cause this preexisting issue).

Comment on lines 106 to 112
details: {
clickLabel: 'Dismiss Banner',
headline: this.headline,
showClose: this.showClose,
windowSession: this.windowSession,
type: this.type,
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We may want to ask @bsmartin-ep what kinds of data we should capture in the analytics event for this since <Banner> just had a simple recordEvent().

Comment on lines 119 to 137
private handleAlertBodyClick(e: MouseEvent): void {
if (!this.disableAnalytics) {
const target = e.target as HTMLElement;
// If it's a link being clicked, dispatch an analytics event
if (target?.tagName === 'A') {
const detail = {
componentName: 'Banner',
action: 'linkClick',
details: {
clickLabel: target.innerText,
headline: this.headline,
showClose: this.showClose,
type: this.type,
},
};
this.componentLibraryAnalytics.emit(detail);
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like a simplified version of what's already happening in the <va-alert> with the componentName changed. Do we also need to do this for the banner?

private handleAlertBodyClick(e: MouseEvent): void {
let headlineText = null;
// This is the happy path, meaning the user isn't using IE11
try {
const children = this.el.shadowRoot.querySelector('slot').assignedNodes();
// An empty array means that there isn't a node with `slot="headline"`
headlineText = children.length > 0 ? children[0].textContent : null;
} catch (e) {
// This is where we handle the edge case of the user being on IE11
const children = this.el.shadowRoot.childNodes;
const headerList = children.filter((node: any) =>
['h1', 'h2', 'h3', 'h4', 'h5', 'h6'].includes(
node.tagName.toLowerCase(),
),
);
headlineText = headerList.length > 0 ? headerList[0].textContent : null;
}
if (!this.disableAnalytics) {
const target = e.target as HTMLElement;
// If it's a link being clicked, dispatch an analytics event
if (target?.tagName === 'A') {
const detail = {
componentName: 'AlertBox',
action: 'linkClick',
details: {
clickLabel: target.innerText,
headline: headlineText,
status: this.status,
backgroundOnly: this.backgroundOnly,
closeable: this.closeable,
},
};
this.componentLibraryAnalytics.emit(detail);
}
}
}

If we do need to do it, maybe we could have a handler for the component-library-analytics event which stops the event from bubbling with .stopPropogation() then fires out a new analytics event. This event could have the same data except componentName gets changed to va-banner.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bkjohnson good point on the double analytics event firing. I had added it in because in the React Component version of the Banner we had a recordEvent() being fired when an A tag was clicked in the banner here:

event: 'nav-warning-alert-box-content-link-click',

But since we are already tracking that event in the Alert Web component I guess we probably don't need it in the banner. Do you think we should have different events for the A tag clicking in the different components or is this a larger question that we need to discuss with the Analytics team?

Comment on lines 147 to 151
// Derive dismissed banners from storage.
const dismissedBannersString = storageType.getItem(DISMISSED_BANNERS_KEY);
this.dismissedBanners = dismissedBannersString
? JSON.parse(dismissedBannersString)
: [];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to do this if showClose is false?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I updated it to wrap it in a conditional so that it will only fire if showClose is true

status={this.type}
>
<h3 slot="headline">{this.headline}</h3>
<slot></slot>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm thankful that once this migration happens we won't be using dangerouslySetInnerHTML 😅

Comment on lines 76 to 97
it('fires an analytics event when a link is clicked', async () => {
const page = await newE2EPage();
await page.setContent(
'<va-banner headline="This is a test">Test Content<a href="#">Test Link</a></va-banner>',
);

const analyticsSpy = await page.spyOnEvent('component-library-analytics');

const link = await page.find('va-banner a');
await link.click();

expect(analyticsSpy).toHaveReceivedEventDetail({
componentName: 'Banner',
action: 'linkClick',
details: {
clickLabel: 'Test Link',
headline: 'This is a test',
showClose: false,
type: 'info',
},
});
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would component-library-analytics be firing twice - once from the alert and once from the banner? If that's the behavior we prefer, let's also test for both events firing.

Copy link
Contributor

@andresriveratoro andresriveratoro left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM


return (
<Host
data-e2e-id="emergency-banner"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might not be necessary anymore. It may have been in <Banner> originally for some nightwatch tests, but I'm not seeing emergency-banner used anywhere in vets-website cypress tests.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excellent going to remove

@pdavies88 pdavies88 merged commit e157e3c into master Mar 1, 2022
@pdavies88 pdavies88 deleted the 35102/Banner-Web-Component branch March 1, 2022 16:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
minor For a minor Semantic Version change
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Banner web component: development
4 participants