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

Merged
merged 17 commits into from
Nov 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions packages/storybook/stories/va-language-toggle.stories.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import React, { Fragment } from 'react';
import { getWebComponentDocs, StoryDocs, propStructure } from './wc-helpers';
import { VaLanguageToggle } from '@department-of-veterans-affairs/web-components/react-bindings';

VaLanguageToggle.displayName = 'VaLanguageToggle';

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 Template = ({}) => {
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={url.href}
esHref={url.href}
tlHref={url.href}
onVaLanguageToggle={handleLanguageToggle}
/>
);
};

const WithRouterLinksTemplate = ({}) => {

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>
<br/>
<VaLanguageToggle
enHref={url.href}
esHref={url.href}
tlHref={url.href}
routerLinks={true}
onVaLanguageToggle={handleLanguageToggle}
/>
</Fragment>
)
}

export const Default = Template.bind(null);
Default.argTypes = propStructure(languageToggleDocs);

export const WithRouterLinks = WithRouterLinksTemplate.bind(null);
104 changes: 103 additions & 1 deletion 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 @@ -698,6 +725,10 @@ export namespace Components {
* Adds an aria-label attribute to the link element.
*/
"label"?: string;
/**
* The lang attribute for the anchor tag in the Default va-link. Also used for hreflang.
*/
"language"?: string;
/**
* The number of pages of the file. Only displayed if download is `true`.
*/
Expand Down Expand Up @@ -1825,6 +1856,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 @@ -2330,6 +2365,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 @@ -2972,6 +3030,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 @@ -3730,6 +3789,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 @@ -3793,8 +3887,9 @@ declare namespace LocalJSX {
*/
"label"?: string;
/**
* The event used to track usage of the component.
* The lang attribute for the anchor tag in the Default va-link. Also used for hreflang.
*/
"language"?: string;
"onComponent-library-analytics"?: (event: VaLinkCustomEvent<any>) => void;
/**
* The number of pages of the file. Only displayed if download is `true`.
Expand Down Expand Up @@ -5031,6 +5126,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 @@ -5206,6 +5302,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 >>> va-link');
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 >>> va-link');
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,21 @@
:host div {
display: inline-block;

div.inner-div {
border-right: 1px solid var(--vads-color-base-light);
padding-right: 8px;
margin-right: 8px;
}

va-link {
&::part(anchor) {
text-decoration: none;
}
&.is-current-lang {
font-weight: var(--font-weight-bold);
}
color: var(--vads-color-link);
padding-bottom: 2px;
border-bottom: 1px solid var(--vads-color-link);
}
}
Loading
Loading