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

va-language-toggle: add component #1386

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
75 changes: 75 additions & 0 deletions packages/storybook/stories/va-language-toggle.stories.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import React, { Fragment } from 'react';
import { getWebComponentDocs, StoryDocs, propStructure } from './wc-helpers';
import { VaLanguageToggle } from '@department-of-veterans-affairs/web-components/react-bindings';

const languageToggleDocs = getWebComponentDocs('va-language-toggle');

export default {
title: 'Components/Language Toggle',
id: 'components/va-language-toggle',
parameters: {
componentSubtitle: 'va-language-toggle web component',
docs: {
page: () => <StoryDocs storyDefault={Default} data={languageToggleDocs} />,
},
},
};

const url = new URL(window.parent.location.href);
url.searchParams.set('path', '/docs/components-va-language-toggle--docs');

const defaultArgs = {
enHref: url.href,
esHref: url.href,
tlHref: url.href,
Copy link
Contributor

Choose a reason for hiding this comment

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

Any idea why these props are showing up on the Storybook page like this? I don't think other pages do that.

Screenshot 2024-11-06 at 8 37 19 AM

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@jamigibbs - fixed!

Copy link
Contributor

@jamigibbs jamigibbs Nov 7, 2024

Choose a reason for hiding this comment

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

@it-harrison Awesome! Thanks for figuring that out! I think the only other thing that needs to be done is passing in an empty arguments object to the Template function so that the code examples populate:

Screenshot 2024-11-07 at 8 42 32 AM

Maybe something like this here on this line and this line too? I can't exactly remember what has fixed this in the past.

const Template = ({}) => {
 ...
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@jamigibbs - good catch, fixed!

}

const Template = ({ enHref, esHref, tlHref }) => {
let lang = sessionStorage.getItem('va-language-toggle-lang') ?? 'en';
function handleLanguageToggle(e) {
const { language } = e.detail;
sessionStorage.setItem('va-language-toggle-lang', language)
}

return (
<VaLanguageToggle
language={lang}
enHref={enHref}
esHref={esHref}
tlHref={tlHref}
onVaLanguageToggle={handleLanguageToggle}
/>
);
};

const WithRouterLinksTemplate = ({ enHref, esHref, tlHref }) => {

function handleLanguageToggle(e) {
console.log(`the language has been toggled to ${e.detail.language}`);
}

return (
<Fragment>
<div>This example illustrates how to use the component with a router. When <code>router-links</code> is
set to <code>true</code>, clicking on a link will not navigate to a new page (i.e. <code>event.preventDefault()</code> is called).
By capturing the <code>language-toggle</code> event page content can be updated as needed to reflect the selected language.
</div>
<VaLanguageToggle
enHref={enHref}
esHref={esHref}
tlHref={tlHref}
routerLinks={true}
onVaLanguageToggle={handleLanguageToggle}
/>
</Fragment>
)
}

export const Default = Template.bind(null);
Default.args = {
...defaultArgs,
};
Default.argTypes = propStructure(languageToggleDocs);

export const WithRouterLinks = WithRouterLinksTemplate.bind(null);
WithRouterLinks.args = { ...defaultArgs }
97 changes: 97 additions & 0 deletions packages/web-components/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -636,6 +636,33 @@ export namespace Components {
*/
"srtext"?: string;
}
/**
* @componentName Language Toggle
* @maturityCategory caution
* @maturityLevel candidate
*/
interface VaLanguageToggle {
/**
* The English language href for the page. Required.
*/
"enHref": string;
/**
* The Spanish language href for the page. Optional.
*/
"esHref"?: string;
/**
* The ISO language code for the page. Default is 'en'.
*/
"language": string;
/**
* If true, specifies that the toggle is being used on a page with a router and clicking on a link will not result in page navigation.
*/
"routerLinks"?: boolean;
/**
* The Tagalog language href for the page. Optional.
*/
"tlHref"?: string;
}
/**
* @componentName Link
* @maturityCategory caution
Expand Down Expand Up @@ -1821,6 +1848,10 @@ export interface VaFileInputMultipleCustomEvent<T> extends CustomEvent<T> {
detail: T;
target: HTMLVaFileInputMultipleElement;
}
export interface VaLanguageToggleCustomEvent<T> extends CustomEvent<T> {
detail: T;
target: HTMLVaLanguageToggleElement;
}
export interface VaLinkCustomEvent<T> extends CustomEvent<T> {
detail: T;
target: HTMLVaLinkElement;
Expand Down Expand Up @@ -2326,6 +2357,29 @@ declare global {
prototype: HTMLVaIconElement;
new (): HTMLVaIconElement;
};
interface HTMLVaLanguageToggleElementEventMap {
"vaLanguageToggle": any;
"component-library-analytics": any;
}
/**
* @componentName Language Toggle
* @maturityCategory caution
* @maturityLevel candidate
*/
interface HTMLVaLanguageToggleElement extends Components.VaLanguageToggle, HTMLStencilElement {
addEventListener<K extends keyof HTMLVaLanguageToggleElementEventMap>(type: K, listener: (this: HTMLVaLanguageToggleElement, ev: VaLanguageToggleCustomEvent<HTMLVaLanguageToggleElementEventMap[K]>) => any, options?: boolean | AddEventListenerOptions): void;
addEventListener<K extends keyof DocumentEventMap>(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
addEventListener<K extends keyof HTMLElementEventMap>(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
removeEventListener<K extends keyof HTMLVaLanguageToggleElementEventMap>(type: K, listener: (this: HTMLVaLanguageToggleElement, ev: VaLanguageToggleCustomEvent<HTMLVaLanguageToggleElementEventMap[K]>) => any, options?: boolean | EventListenerOptions): void;
removeEventListener<K extends keyof DocumentEventMap>(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
removeEventListener<K extends keyof HTMLElementEventMap>(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
}
var HTMLVaLanguageToggleElement: {
prototype: HTMLVaLanguageToggleElement;
new (): HTMLVaLanguageToggleElement;
};
interface HTMLVaLinkElementEventMap {
"component-library-analytics": any;
}
Expand Down Expand Up @@ -2968,6 +3022,7 @@ declare global {
"va-file-input-multiple": HTMLVaFileInputMultipleElement;
"va-header-minimal": HTMLVaHeaderMinimalElement;
"va-icon": HTMLVaIconElement;
"va-language-toggle": HTMLVaLanguageToggleElement;
"va-link": HTMLVaLinkElement;
"va-link-action": HTMLVaLinkActionElement;
"va-loading-indicator": HTMLVaLoadingIndicatorElement;
Expand Down Expand Up @@ -3726,6 +3781,41 @@ declare namespace LocalJSX {
*/
"srtext"?: string;
}
/**
* @componentName Language Toggle
* @maturityCategory caution
* @maturityLevel candidate
*/
interface VaLanguageToggle {
/**
* The English language href for the page. Required.
*/
"enHref": string;
/**
* The Spanish language href for the page. Optional.
*/
"esHref"?: string;
/**
* The ISO language code for the page. Default is 'en'.
*/
"language"?: string;
/**
* The event used to track usage of the component.
*/
"onComponent-library-analytics"?: (event: VaLanguageToggleCustomEvent<any>) => void;
/**
* Event fired when a link is clicked. Includes the selected language's ISO code.
*/
"onVaLanguageToggle"?: (event: VaLanguageToggleCustomEvent<any>) => void;
/**
* If true, specifies that the toggle is being used on a page with a router and clicking on a link will not result in page navigation.
*/
"routerLinks"?: boolean;
/**
* The Tagalog language href for the page. Optional.
*/
"tlHref"?: string;
}
/**
* @componentName Link
* @maturityCategory caution
Expand Down Expand Up @@ -5023,6 +5113,7 @@ declare namespace LocalJSX {
"va-file-input-multiple": VaFileInputMultiple;
"va-header-minimal": VaHeaderMinimal;
"va-icon": VaIcon;
"va-language-toggle": VaLanguageToggle;
"va-link": VaLink;
"va-link-action": VaLinkAction;
"va-loading-indicator": VaLoadingIndicator;
Expand Down Expand Up @@ -5198,6 +5289,12 @@ declare module "@stencil/core" {
* @maturityLevel candidate
*/
"va-icon": LocalJSX.VaIcon & JSXBase.HTMLAttributes<HTMLVaIconElement>;
/**
* @componentName Language Toggle
* @maturityCategory caution
* @maturityLevel candidate
*/
"va-language-toggle": LocalJSX.VaLanguageToggle & JSXBase.HTMLAttributes<HTMLVaLanguageToggleElement>;
/**
* @componentName Link
* @maturityCategory caution
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { newE2EPage } from "@stencil/core/testing";
import { axeCheck } from "../../../testing/test-helpers";

describe('va-language-toggle', () => {
const enHref = "/resources/the-pact-act-and-your-va-benefits/";
const esHref = "/resources/the-pact-act-and-your-va-benefits-esp/"
const tlHref = "/resources/the-pact-act-and-your-va-benefits-tl/"

it('renders', async () => {
const page = await newE2EPage();
await page.setContent(`<va-language-toggle en-href="${enHref}" es-href="${esHref}" tl-href="${tlHref}" />`);
const element = await page.find('va-language-toggle');
expect(element).toHaveClass('hydrated');
});

it('English is the default language', async () => {
const page = await newE2EPage();
await page.setContent(`<va-language-toggle en-href="${enHref}" es-href="${esHref}" tl-href="${tlHref}" />`);
const anchor = await page.find('va-language-toggle >>> a');
expect(anchor).toHaveClass('is-current-lang');
});

it('only renders links for those languages with supplied hrefs', async () => {
const page = await newE2EPage();
await page.setContent(`<va-language-toggle en-href="${enHref}" es-href="${esHref}" />`);
const anchors = await page.findAll('va-language-toggle >>> a');
expect(anchors).toHaveLength(2);
})

it('if language prop is set the matching language is bolded', async () => {
const page = await newE2EPage();
await page.setContent(`<va-language-toggle language="es" en-href="${enHref}" es-href="${esHref}" tl-href="${tlHref}" />`);
const [_, anchor] = await page.findAll('va-language-toggle >>> a');
expect(anchor).toHaveClass('is-current-lang');
});

it('if router-links is set, clicking an anchor tag does not result in page navigation', async () => {
const page = await newE2EPage();
await page.setContent(`<va-language-toggle router-links="true" en-href="${enHref}" es-href="${esHref}" tl-href="${tlHref}" />`);
const [startUrl] = page.url().split('?');
const [_, anchor] = await page.findAll('va-language-toggle >>> a');
await anchor.click();
const [endUrl] = page.url().split('?');
expect(startUrl).toEqual(endUrl);
});

it('fires a language-toggle event when a link is clicked', async () => {
const page = await newE2EPage();
await page.setContent(`<va-language-toggle en-href="${enHref}" es-href="${esHref}" tl-href="${tlHref}" />`);
const toggleSpy = await page.spyOnEvent('vaLanguageToggle');
const [_, anchor] = await page.findAll('va-language-toggle >>> a');
await anchor.click();
expect(toggleSpy).toHaveReceivedEvent();
});

it('fires a component-analytics event when a link is clicked', async () => {
const page = await newE2EPage();
await page.setContent(`<va-language-toggle en-href="${enHref}" es-href="${esHref}" tl-href="${tlHref}" />`);
const toggleSpy = await page.spyOnEvent('component-library-analytics');
const anchor = await page.find('va-language-toggle >>> a');
await anchor.click();
expect(toggleSpy).toHaveReceivedEvent();
});

it('passes an aXe check', async () => {
const page = await newE2EPage();
await page.setContent(`<va-language-toggle en-href="${enHref}" es-href="${esHref}" tl-href="${tlHref}" />`);
await axeCheck(page);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
@import '../../mixins/focusable.css';

:host {
display: inline-block;
margin: 32px 0 24px 0;
border-bottom: 1px solid var(--vads-color-base-dark);

span {
margin: 0 4px 0 4px;
color: var(--vads-color-base-dark);
}

a {
&.is-current-lang {
font-weight: var(--font-weight-bold);
text-decoration: none;
color: var(--vads-color-base);
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 have a Figma link that can be added to description of the PR? I think it would be worthwhile to get a review from one of the designers as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@jamigibbs I'm reaching out to Barb on this

}
&:hover {
background-color: rgba(0, 0, 0, 0.05);
}
}
}
Loading
Loading