@salesforcedevs/docs-components
Advanced tools
Comparing version
{ | ||
"name": "@salesforcedevs/docs-components", | ||
"version": "0.4.48", | ||
"version": "0.5.0-alpha1", | ||
"description": "Docs Lightning web components for DSC", | ||
@@ -17,3 +17,3 @@ "license": "MIT", | ||
}, | ||
"gitHead": "bece6881f38e566196a3c0720eb3b7c1573b8913" | ||
"gitHead": "4629fdd9ca18a13480044ad43515b91945d16aad" | ||
} |
@@ -13,5 +13,7 @@ import Button from "dx/button"; | ||
import Dropdown from "dx/dropdown"; | ||
import { Option } from "typings/custom"; | ||
const EXPECTED_QUERY_TABLET = "(max-width: 980px)"; | ||
const EXPECTED_QUERY_MOBILE = "(max-width: 880px)"; | ||
const SMALL_MOBILE_MATCH = "(max-width: 740px)"; | ||
@@ -21,5 +23,64 @@ const TAG = "doc-header"; | ||
const assertMediaMatchCalls = () => { | ||
expect(window.matchMedia).toBeCalledTimes(3); | ||
expect(window.matchMedia).toHaveBeenNthCalledWith(1, EXPECTED_QUERY_MOBILE); | ||
expect(window.matchMedia).toHaveBeenNthCalledWith(2, EXPECTED_QUERY_TABLET); | ||
expect(window.matchMedia).toHaveBeenLastCalledWith(SMALL_MOBILE_MATCH); | ||
}; | ||
const assertDropdownRender = ( | ||
element: Header, | ||
dropdown: Dropdown, | ||
languages: Array<Option> | ||
) => { | ||
expect(dropdown).not.toBeNull(); | ||
expect(dropdown.classList).toHaveLength(1); | ||
expect(dropdown.classList).toContain("header_lang-dropdown"); | ||
expect(dropdown.options).toHaveLength(languages.length); | ||
dropdown.options.forEach((option, index) => { | ||
const mockLang = languages[index]; | ||
expect(option).toHaveProperty("id", mockLang.id); | ||
expect(option).toHaveProperty("label", mockLang.label); | ||
}); | ||
expect(dropdown.valuePath).toBe(element.langValuePath); | ||
expect(dropdown.value).toBe(element.language); | ||
expect(dropdown.querySelector("dx-button")).not.toBeNull(); | ||
}; | ||
const testLangEvent = () => { | ||
const { languages } = mockPropsDevelopers; | ||
const [firstLang, secondLang] = languages; | ||
const element = render({ | ||
...mockPropsDevelopers, | ||
language: firstLang.id | ||
}); | ||
expect(element.language).toBe(firstLang.id); | ||
const dropdown: HTMLElement = element.shadowRoot.querySelector( | ||
".header_lang-dropdown" | ||
); | ||
const mockLangChange = jest.fn(); | ||
element.addEventListener("langchange", mockLangChange); | ||
dropdown.dispatchEvent( | ||
new CustomEvent("change", { detail: secondLang.id }) | ||
); | ||
expect(element.language).toBe(secondLang.id); | ||
expect(mockLangChange).toBeCalledTimes(1); | ||
expect(mockLangChange.mock.calls[0][0]).toHaveProperty( | ||
"detail", | ||
secondLang.id | ||
); | ||
return expect(element).toBeAccessible(); | ||
}; | ||
describe(TAG, () => { | ||
afterEach(() => { | ||
jest.clearAllMocks(); | ||
afterAll(() => { | ||
jest.resetAllMocks(); | ||
}); | ||
@@ -29,9 +90,55 @@ | ||
it("renders", () => { | ||
const element = render(); | ||
const headerEl: HTMLElement = element.shadowRoot.querySelector( | ||
"header" | ||
); | ||
expect(headerEl).not.toBeNull(); | ||
expect(headerEl.classList).toHaveLength(0); | ||
expect(headerEl.querySelector("dx-feedback-banner")).not.toBeNull(); | ||
const logo = element.shadowRoot.querySelector("dx-logo"); | ||
expect(logo).not.toBeNull(); | ||
expect(logo.label).toBe("Salesforce"); | ||
expect( | ||
element.shadowRoot.querySelector("dx-header-nav") | ||
).toBeNull(); | ||
expect( | ||
element.shadowRoot.querySelector("dx-header-search") | ||
).toBeNull(); | ||
expect( | ||
element.shadowRoot.querySelector("dx-header-mobile-nav-menu") | ||
).toBeNull(); | ||
const homeLink: HTMLAnchorElement = element.shadowRoot.querySelector( | ||
".home-link" | ||
); | ||
expect(homeLink).not.toBeNull(); | ||
expect(homeLink.href).toMatch(/\/$/); | ||
expect(homeLink.querySelector("dx-icon")).toBeNull(); | ||
expect(element.shadowRoot.querySelector("dx-dropdown")).toBeNull(); | ||
expect( | ||
element.shadowRoot.querySelector( | ||
".header_l2_group.header_l2_group-nav" | ||
) | ||
).toBeNull(); | ||
}); | ||
it("renders with some properties", () => { | ||
const subtitle = "testsubtitle"; | ||
const element = render({ ...mockPropsDevelopers, subtitle }); | ||
const subtitleEl = element.shadowRoot.querySelector(".subtitle"); | ||
const buttons: Array<HTMLElement> = element.shadowRoot.querySelectorAll( | ||
"dx-button" | ||
expect(subtitleEl.textContent).toEqual(subtitle); | ||
const signUp: HTMLElement = element.shadowRoot.querySelector( | ||
".header-login-signup dx-button" | ||
); | ||
const dropdown = element.shadowRoot.querySelector("dx-dropdown"); | ||
expect(signUp).not.toBeNull(); | ||
const headerNavs: Array<HeaderNav> = element.shadowRoot.querySelectorAll( | ||
@@ -44,6 +151,18 @@ "dx-header-nav" | ||
expect(subtitleEl.textContent).toEqual(subtitle); | ||
expect(buttons).toHaveLength(1); | ||
expect(dropdown).toBeNull(); | ||
const dropdown: Dropdown = element.shadowRoot.querySelector( | ||
".header_l2_group.header_l2_group-right-ctas dx-dropdown" | ||
); | ||
assertDropdownRender( | ||
element, | ||
dropdown, | ||
mockPropsDevelopers.languages | ||
); | ||
expect( | ||
element.shadowRoot.querySelector( | ||
".header_l2_group.header_l2_group-title dx-dropdown" | ||
) | ||
).toBeNull(); | ||
const brandIcon = element.shadowRoot.querySelector(".brand-icon"); | ||
@@ -102,41 +221,5 @@ expect(brandIcon).toBeNull(); | ||
it("receives versions", () => { | ||
const version = { id: "123", label: "Test" }; | ||
const element = render({ | ||
...mockPropsDevelopers, | ||
versions: JSON.stringify([version]) | ||
}); | ||
const dropdown: Dropdown = element.shadowRoot.querySelector( | ||
"dx-dropdown" | ||
); | ||
expect(dropdown).not.toBeNull(); | ||
expect(dropdown.options).toHaveLength(1); | ||
// eslint-disable-next-line jest/expect-expect | ||
it("triggers event on language change", testLangEvent); | ||
const [option] = dropdown.options; | ||
expect(option).toHaveProperty("id", version.id); | ||
expect(option).toHaveProperty("label", version.label); | ||
return expect(element).toBeAccessible(); | ||
}); | ||
it("triggers event on version change", () => { | ||
const versionsListParameter = [{ id: "123", label: "Test" }]; | ||
const element = render({ | ||
...mockPropsDevelopers, | ||
versions: JSON.stringify(versionsListParameter) | ||
}); | ||
const dropdown: HTMLElement = element.shadowRoot.querySelector( | ||
".header_version-dropdown" | ||
); | ||
const mockVersionChange = jest.fn(); | ||
element.addEventListener("versionchange", mockVersionChange); | ||
dropdown.dispatchEvent( | ||
new CustomEvent("change", { detail: "123" }) | ||
); | ||
expect(mockVersionChange).toBeCalledTimes(1); | ||
expect(mockVersionChange.mock.calls[0][0].detail).toEqual("123"); | ||
return expect(element).toBeAccessible(); | ||
}); | ||
it("triggers event onrequestopennav", () => { | ||
@@ -208,12 +291,6 @@ const element = render({ | ||
describe("tablet", () => { | ||
const oldMatchMedia = window.matchMedia; | ||
beforeAll(() => { | ||
window.matchMedia = createMediaMock(false, [false, true]); | ||
beforeEach(() => { | ||
window.matchMedia = createMediaMock(false, [false, true, false]); | ||
}); | ||
afterAll(() => { | ||
window.matchMedia = oldMatchMedia; | ||
}); | ||
it("renders tablet specific elements", () => { | ||
@@ -237,10 +314,14 @@ const element = render(mockPropsDevelopers); | ||
expect(signupDiv).not.toBeNull(); | ||
expect( | ||
element.shadowRoot.querySelector( | ||
".header_l2_group.header_l2_group-right-ctas dx-dropdown" | ||
) | ||
).not.toBeNull(); | ||
expect( | ||
element.shadowRoot.querySelector( | ||
".header_l2_group.header_l2_group-title dx-dropdown" | ||
) | ||
).toBeNull(); | ||
expect(window.matchMedia).toBeCalledTimes(2); | ||
expect(window.matchMedia).toHaveBeenCalledWith( | ||
EXPECTED_QUERY_MOBILE | ||
); | ||
expect(window.matchMedia).toHaveBeenLastCalledWith( | ||
EXPECTED_QUERY_TABLET | ||
); | ||
assertMediaMatchCalls(); | ||
}); | ||
@@ -250,12 +331,6 @@ }); | ||
describe("mobile", () => { | ||
const oldMatchMedia = window.matchMedia; | ||
beforeAll(() => { | ||
window.matchMedia = createMediaMock(true); | ||
beforeEach(() => { | ||
window.matchMedia = createMediaMock(false, [true, true, false]); | ||
}); | ||
afterAll(() => { | ||
window.matchMedia = oldMatchMedia; | ||
}); | ||
it("renders mobile specific elements", () => { | ||
@@ -272,18 +347,21 @@ const element = render(mockPropsDevelopers); | ||
); | ||
expect( | ||
element.shadowRoot.querySelector( | ||
".header_l2_group.header_l2_group-right-ctas dx-dropdown" | ||
) | ||
).not.toBeNull(); | ||
expect( | ||
element.shadowRoot.querySelector( | ||
".header_l2_group.header_l2_group-title dx-dropdown" | ||
) | ||
).toBeNull(); | ||
expect(headerSearch).not.toBeNull(); | ||
expect(headerSearch.mobile).toBe(true); | ||
expect(window.matchMedia).toBeCalledTimes(2); | ||
expect(window.matchMedia).toHaveBeenCalledWith( | ||
EXPECTED_QUERY_MOBILE | ||
); | ||
expect(window.matchMedia).toHaveBeenLastCalledWith( | ||
EXPECTED_QUERY_TABLET | ||
); | ||
assertMediaMatchCalls(); | ||
}); | ||
it("tests toggle button", () => { | ||
const element = render({ | ||
...mockPropsDevelopers | ||
}); | ||
const element = render(mockPropsDevelopers); | ||
const button: HTMLElement = element.shadowRoot.querySelector( | ||
@@ -307,2 +385,33 @@ ".nav_menu-ctas .nav_menu-button" | ||
}); | ||
describe("small mobile", () => { | ||
beforeEach(() => { | ||
window.matchMedia = createMediaMock(true); | ||
}); | ||
it("renders small mobile specific elements", () => { | ||
const element = render(mockPropsDevelopers); | ||
const dropdown = element.shadowRoot.querySelector( | ||
".header_l2_group.header_l2_group-title dx-dropdown" | ||
); | ||
expect(dropdown).not.toBeNull(); | ||
assertDropdownRender( | ||
element, | ||
dropdown, | ||
mockPropsDevelopers.languages | ||
); | ||
expect( | ||
element.shadowRoot.querySelector( | ||
".header_l2_group.header_l2_group-right-ctas dx-dropdown" | ||
) | ||
).toBeNull(); | ||
return expect(element).toBeAccessible(); | ||
}); | ||
// eslint-disable-next-line jest/expect-expect | ||
it("triggers event on language change", testLangEvent); | ||
}); | ||
}); |
@@ -17,19 +17,12 @@ import { | ||
import mockNavDevelopers from "./mockNavDevelopers"; | ||
import { Option } from "typings/custom"; | ||
const versions = [ | ||
const languages: Option[] = [ | ||
{ | ||
id: "1", | ||
label: "v1" | ||
id: "en-us", | ||
label: "English" | ||
}, | ||
{ | ||
id: "2", | ||
label: "v2" | ||
}, | ||
{ | ||
id: "3", | ||
label: "v3" | ||
}, | ||
{ | ||
id: "4", | ||
label: "v4" | ||
id: "ja-jp", | ||
label: "日本語" | ||
} | ||
@@ -42,2 +35,3 @@ ]; | ||
subtitle: "Apex Developer Guides", | ||
languages, | ||
...coveoConfig | ||
@@ -57,5 +51,3 @@ }; | ||
scopedNavItems: mockNavEmployees, | ||
subtitle: "Employees", | ||
version: "1", | ||
versions | ||
subtitle: "Employees" | ||
}; | ||
@@ -69,5 +61,3 @@ | ||
scopedNavItems: mockNavMarketing, | ||
subtitle: "Marketing", | ||
version: "1", | ||
versions | ||
subtitle: "Marketing" | ||
}; | ||
@@ -81,5 +71,3 @@ | ||
scopedNavItems: mockNavPartners, | ||
subtitle: "Partners", | ||
version: "1", | ||
versions | ||
subtitle: "Partners" | ||
}; | ||
@@ -93,5 +81,3 @@ | ||
scopedNavItems: mockNavCommerce, | ||
subtitle: "Commerce", | ||
version: "1", | ||
versions | ||
subtitle: "Commerce" | ||
}; | ||
@@ -105,5 +91,3 @@ | ||
scopedNavItems: mockNavSales, | ||
subtitle: "Sales", | ||
version: "1", | ||
versions | ||
subtitle: "Sales" | ||
}; | ||
@@ -117,5 +101,3 @@ | ||
scopedNavItems: mockNavSuccess, | ||
subtitle: "Success", | ||
version: "1", | ||
versions | ||
subtitle: "Success" | ||
}; | ||
@@ -129,5 +111,3 @@ | ||
scopedNavItems: mockNavIntegration, | ||
subtitle: "Integration", | ||
version: "1", | ||
versions | ||
subtitle: "Integration" | ||
}; | ||
@@ -141,5 +121,3 @@ | ||
scopedNavItems: mockNavPlatform, | ||
subtitle: "Platform", | ||
version: "1", | ||
versions | ||
subtitle: "Platform" | ||
}; | ||
@@ -153,5 +131,3 @@ | ||
scopedNavItems: mockNavIndustries, | ||
subtitle: "Industries", | ||
version: "1", | ||
versions | ||
subtitle: "Industries" | ||
}; | ||
@@ -165,5 +141,3 @@ | ||
scopedNavItems: mockNavLearning, | ||
subtitle: "Learning", | ||
version: "1", | ||
versions | ||
subtitle: "Learning" | ||
}; | ||
@@ -177,5 +151,3 @@ | ||
scopedNavItems: mockNavService, | ||
subtitle: "Service", | ||
version: "1", | ||
versions | ||
subtitle: "Service" | ||
}; | ||
@@ -189,5 +161,3 @@ | ||
scopedNavItems: mockNavAnalytics, | ||
subtitle: "Analytics", | ||
version: "1", | ||
versions | ||
subtitle: "Analytics" | ||
}; |
@@ -18,5 +18,28 @@ import { html } from "lit-html"; | ||
const controlLang = mockPropsDevelopers.languages.map(({ id }) => id); | ||
export default { | ||
title: "docs/doc-header", | ||
component: "doc-header" | ||
component: "doc-header", | ||
argTypes: { | ||
bailHref: { | ||
defaultValue: "/quip-dev-center", | ||
control: { | ||
type: "text" | ||
} | ||
}, | ||
bailLabel: { | ||
defaultValue: "PDF", | ||
control: { | ||
type: "text" | ||
} | ||
}, | ||
language: { | ||
defaultValue: controlLang[0], | ||
control: { | ||
options: controlLang, | ||
type: "select" | ||
} | ||
} | ||
} | ||
}; | ||
@@ -62,3 +85,4 @@ | ||
mockProps: any, | ||
brand: string | ||
brand: string, | ||
args: any | ||
) => html` ${styles()} ${renderPreventNavScript()} | ||
@@ -75,7 +99,7 @@ <div class="fake-url">/</div> | ||
nav-items="${JSON.stringify(mockProps.navItems)}" | ||
languages="${JSON.stringify(mockProps.languages)}" | ||
language="${args.language}" | ||
scoped-nav-items="${JSON.stringify(mockProps.scopedNavItems)}" | ||
versions="${JSON.stringify(mockProps.versions)}" | ||
version="1" | ||
bail-href="/quip-dev-center" | ||
bail-label="Quip Center" | ||
bail-href="${args.bailHref}" | ||
bail-label="${args.bailLabel}" | ||
brand="${brand}" | ||
@@ -85,3 +109,3 @@ onclick="headerClick(event)" | ||
export const Base = () => html` | ||
export const Base = (args: any) => html` | ||
${styles()} ${renderPreventNavScript()} | ||
@@ -91,6 +115,8 @@ <div class="fake-url">/</div> | ||
title="${mockPropsDevelopers.title}" | ||
bail-href="/" | ||
bail-label="PDF" | ||
bail-href="${args.bailHref}" | ||
bail-label="${args.bailLabel}" | ||
subtitle="${mockPropsDevelopers.subtitle}" | ||
nav-items="${JSON.stringify(mockPropsDevelopers.navItems)}" | ||
languages="${JSON.stringify(mockPropsDevelopers.languages)}" | ||
language="${args.language}" | ||
coveo-organization-id="${mockPropsDevelopers.coveoOrganizationId}" | ||
@@ -105,33 +131,36 @@ coveo-public-access-token="${mockPropsDevelopers.coveoPublicAccessToken}" | ||
// BRAND STORIES | ||
export const Employees = () => | ||
headerStoryGenerator(mockPropsEmployees, "employees"); | ||
export const Employees = (args: any) => | ||
headerStoryGenerator(mockPropsEmployees, "employees", args); | ||
export const Marketing = () => | ||
headerStoryGenerator(mockPropsMarketing, "marketing"); | ||
export const Marketing = (args: any) => | ||
headerStoryGenerator(mockPropsMarketing, "marketing", args); | ||
export const Partners = () => | ||
headerStoryGenerator(mockPropsPartners, "partners"); | ||
export const Partners = (args: any) => | ||
headerStoryGenerator(mockPropsPartners, "partners", args); | ||
export const Commerce = () => | ||
headerStoryGenerator(mockPropsCommerce, "commerce"); | ||
export const Commerce = (args: any) => | ||
headerStoryGenerator(mockPropsCommerce, "commerce", args); | ||
export const Sales = () => headerStoryGenerator(mockPropsSales, "sales"); | ||
export const Sales = (args: any) => | ||
headerStoryGenerator(mockPropsSales, "sales", args); | ||
export const Success = () => headerStoryGenerator(mockPropsSuccess, "success"); | ||
export const Success = (args: any) => | ||
headerStoryGenerator(mockPropsSuccess, "success", args); | ||
export const Integration = () => | ||
headerStoryGenerator(mockPropsIntegration, "integration"); | ||
export const Integration = (args: any) => | ||
headerStoryGenerator(mockPropsIntegration, "integration", args); | ||
export const Platform = () => | ||
headerStoryGenerator(mockPropsPlatform, "platform"); | ||
export const Platform = (args: any) => | ||
headerStoryGenerator(mockPropsPlatform, "platform", args); | ||
export const Industries = () => | ||
headerStoryGenerator(mockPropsIndustries, "industries"); | ||
export const Industries = (args: any) => | ||
headerStoryGenerator(mockPropsIndustries, "industries", args); | ||
export const Learning = () => | ||
headerStoryGenerator(mockPropsLearning, "learning"); | ||
export const Learning = (args: any) => | ||
headerStoryGenerator(mockPropsLearning, "learning", args); | ||
export const Service = () => headerStoryGenerator(mockPropsService, "service"); | ||
export const Service = (args: any) => | ||
headerStoryGenerator(mockPropsService, "service", args); | ||
export const Analytics = () => | ||
headerStoryGenerator(mockPropsAnalytics, "analytics"); | ||
export const Analytics = (args: any) => | ||
headerStoryGenerator(mockPropsAnalytics, "analytics", args); |
@@ -6,7 +6,11 @@ import { api } from "lwc"; | ||
import { toJson } from "utils/normalizers"; | ||
import get from "lodash.get"; | ||
const TABLET_MATCH = "980px"; | ||
const MOBILE_MATCH = "880px"; | ||
const SMALL_MOBILE_MATCH = "740px"; | ||
export default class Header extends HeaderBase { | ||
@api langValuePath: string = "id"; // allows to override how language property is interpreted, follows valuePath dropdown api. | ||
@api | ||
@@ -21,5 +25,29 @@ get scopedNavItems() { | ||
@api | ||
get languages() { | ||
return this._languages; | ||
} | ||
set languages(value) { | ||
this._languages = toJson(value); | ||
} | ||
@api | ||
get language() { | ||
return this._language; | ||
} | ||
set language(value) { | ||
if (this._language !== value) { | ||
this._language = value; | ||
} | ||
} | ||
private _language: string | null = null; | ||
private _languages!: Option[]; | ||
private _scopedNavItems!: Option[]; | ||
private smallMobile = false; | ||
private smallMobileMatchMedia!: MediaQueryList; | ||
private tablet = false; | ||
private tabletMatchMedia!: MediaQueryList; | ||
private tablet = false; | ||
@@ -42,6 +70,24 @@ protected mobileBreakpoint(): string { | ||
private get showBailLink(): boolean { | ||
return this.hasBailLink && !this.hasScopedNavItems && !this.mobile; | ||
private get hasLanguages(): boolean { | ||
return !!(this.languages && this.languages.length); | ||
} | ||
private get showMobileLanguages(): boolean { | ||
return this.smallMobile && this.hasLanguages; | ||
} | ||
private get languageLabel(): string { | ||
return ( | ||
(this.language && | ||
this.languages.find( | ||
(lang) => get(lang, this.langValuePath) === this.language | ||
)?.label) || | ||
this.languages[0].label | ||
); | ||
} | ||
private get showMenuButton(): boolean { | ||
return this.mobile && this.hasNavItems; | ||
} | ||
connectedCallback(): void { | ||
@@ -54,2 +100,11 @@ super.connectedCallback(); | ||
this.tabletMatchMedia.addEventListener("change", this.onTabletChange); | ||
this.smallMobileMatchMedia = window.matchMedia( | ||
`(max-width: ${SMALL_MOBILE_MATCH})` | ||
); | ||
this.onSmallMobileChange(this.smallMobileMatchMedia); | ||
this.smallMobileMatchMedia.addEventListener( | ||
"change", | ||
this.onSmallMobileChange | ||
); | ||
} | ||
@@ -63,2 +118,7 @@ | ||
); | ||
this.smallMobileMatchMedia.removeEventListener( | ||
"change", | ||
this.onSmallMobileChange | ||
); | ||
} | ||
@@ -69,2 +129,5 @@ | ||
private onSmallMobileChange = (e: MediaQueryListEvent | MediaQueryList) => | ||
(this.smallMobile = e.matches); | ||
protected additionalClasses(): string { | ||
@@ -76,2 +139,8 @@ return cx( | ||
} | ||
private onLangChange(event: CustomEvent<string>): void { | ||
const { detail } = event; | ||
this._language = detail; | ||
this.dispatchEvent(new CustomEvent("langchange", { detail })); | ||
} | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Mixed license
License(Experimental) Package contains multiple licenses.
Found 1 instance in 1 package
147915
3.87%0
-100%3784
4.53%55
-1.79%