Socket
Socket
Sign inDemoInstall

@salesforcedevs/docs-components

Package Overview
Dependencies
Maintainers
25
Versions
637
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@salesforcedevs/docs-components - npm Package Compare versions

Comparing version 0.0.2 to 0.0.4-beta.0

src/modules/doc/container/__benchmarks__/container.benchmark.js

5

lwc.config.json
{
"modules": [{ "dir": "src/modules" }],
"modules": [
{ "dir": "src/modules" },
{ "npm": "@salesforcedevs/dx-components" }
],
"expose": ["doc/container"]
}

2

package.json
{
"name": "@salesforcedevs/docs-components",
"version": "0.0.2",
"version": "0.0.4-beta.0",
"description": "Docs Lightning web components for DSC",

@@ -5,0 +5,0 @@ "license": "UNLICENSED",

@@ -8,3 +8,3 @@ export default {

return `
<doc-container storybook-content-document-id='${args.selectedContentDocumentId}' storybook-content-id='${args.selectedContentId}'></doc-container>
<doc-container storybook-domain='${args.domain}' storybook-page='${args.page}' storybook-doc-id='${args.docId}' storybook-deliverable='${args.deliverable}' storybook-content-document-id='${args.contentDocumentId}' ></doc-container>
`;

@@ -14,4 +14,7 @@ };

Base.args = {
selectedContentDocumentId: "atlas.en-us.api_action.meta",
selectedContentId: "api_action"
domain: "https://developer.salesforce.com",
page: "docs",
docId: "atlas.en-us.226.0.apexcode.meta",
deliverable: "apexcode",
contentDocumentId: "apex_dev_guide.htm"
};

@@ -1,2 +0,16 @@

import { LightningElement, api, track } from "lwc";
import { LightningElement, api } from "lwc";
import {
AvailableLanguages,
AvailableVersions,
ContentLoaded,
DocContent,
NavigationLoaded,
NormalizedToc,
PageReference,
SelectedVersion,
SelectedLanguage,
SelectedNavigationItem,
DocToc,
PdfUrl
} from "../../../../../../../typings/custom-new";

@@ -7,48 +21,126 @@ const API_MAIN_URL = "https://developer.salesforce.com/docs/get_document/";

/**
* @element doc-container
*/
export default class Container extends LightningElement {
// TODO: We have to evaluate if we can find a better way to do component testing in Storybook.
@api
set storybookContentId(value: string) {
this.selectedContentId = value;
set storybookDomain(value: string) {
this.pageReference.domain = value;
this._isStorybook = true;
}
get storybookContentId() {
return this.selectedContentId;
get storybookDomain() {
return this.pageReference.domain;
}
@api
set storybookPage(value: string) {
this.pageReference.page = value;
}
get storybookPage() {
return this.pageReference.page;
}
@api
set storybookDocId(value: string) {
this.pageReference.docId = value;
}
get storybookDocId() {
return this.pageReference.docId;
}
@api
set storybookDeliverable(value: string) {
this.pageReference.deliverable = value;
}
get storybookDeliverable() {
return this.pageReference.deliverable;
}
@api
set storybookContentDocumentId(value: string) {
this.selectedContentDocumentId = value;
this.pageReference.contentDocumentId = value;
}
get storybookContentDocumentId() {
return this.selectedContentDocumentId;
return this.pageReference.contentDocumentId;
}
@track docData: any; // TODO: We have to define proper TypeScript typings for data payloads
// Navigation
toc: DocToc = [
{
children: [],
text: "",
a_attr: {
href: ""
},
id: ""
}
];
// Decouple navigation from content
// Toolbar
availableLanguages: AvailableLanguages = [
{
label: "",
code: "",
locale: "",
url: ""
}
];
// Navigation
navigationLoaded = false;
contentLoaded = false;
toc: string[] = [];
availableLanguages: string[] = [];
availableVersions: string[] = [];
pdfUrl: string | undefined;
deliverable: string | undefined;
availableVersions: AvailableVersions = [
{
version_test: "",
release_version: "",
doc_version: "",
version_url: ""
}
];
pdfUrl: PdfUrl = "";
selectedLanguage: SelectedLanguage = [
{
label: "",
code: "",
locale: "",
url: ""
}
];
selectedVersion: SelectedVersion = [
{
version_test: "",
release_version: "",
doc_version: "",
version_url: ""
}
];
// Content
@track docContent: any;
docContent: DocContent = "";
// Document selections
// TODO: Impliment dx-tree component
selectedNavigationItem: SelectedNavigationItem = "";
normalizedToc: NormalizedToc = [
{
name: "",
label: "",
items: []
}
];
// Storybook Properties
selectedContentDocumentId: string | undefined;
selectedContentId: string | undefined;
selectedLanguage: string[] = [];
selectedVersion: string[] = [];
selectedVersionUrl: string | undefined;
_isStorybook = false;
// State
navigationLoaded: NavigationLoaded = false;
contentLoaded: ContentLoaded = false;
pageReference: PageReference = {
domain: "",
page: "",
docId: "",
deliverable: "",
contentDocumentId: "",
hash: ""
};
constructor() {

@@ -58,11 +150,11 @@ super();

"navclick",
this.handleNavEvent.bind(this)
this.handleNavEvent.bind(this) // eslint-disableline no-use-before-define
);
this.template.addEventListener(
"versionselected",
this.handleVersionEvent.bind(this)
this.handleToolbarEvent.bind(this) // eslint-disable-line no-use-before-define
);
this.template.addEventListener(
"languageselected",
this.handleLanguageEvent.bind(this)
this.handleToolbarEvent.bind(this) // eslint-disable-line no-use-before-define
);

@@ -72,46 +164,147 @@ }

connectedCallback() {
if (!this.navigationLoaded) {
this.fetchDocData("navigation");
} else {
if (!this._isStorybook) {
this.getPageContext();
}
this.fetchDocData("navigation");
window.onpopstate = () => {
this.getPageContext();
this.fetchDocData("content");
}
};
}
getPageContext() {
[
this.pageReference.domain,
this.pageReference.page,
this.pageReference.docId,
this.pageReference.deliverable,
this.pageReference.contentDocumentId
] = window.location.pathname.split("/");
this.pageReference.hash = window.location.hash;
this.pageReference.domain = `${window.location.protocol}//${window.location.host}`;
//this.updateSelectedItem();
}
handleNavEvent(event: CustomEvent) {
if (event.detail.url) {
let docIdPartials = window.location.pathname.split("/");
if (event.detail.type === "navigation") {
docIdPartials[4] = this.selectedContentId = `${event.detail.url}.htm`;
} else if (event.detail.type === "content") {
docIdPartials[4] = this.selectedContentId = `${event.detail.url}`;
if (event.detail.pageReference) {
const newPageReference = event.detail.pageReference;
const docIdPartials = window.location.pathname.split("/");
if (
this.pageReference.contentDocumentId ===
newPageReference.contentDocumentId &&
newPageReference.hash
) {
this.updatePageReference(newPageReference);
this.rewriteUrl(docIdPartials, newPageReference);
} else {
this.updatePageReference(newPageReference);
this.rewriteUrl(docIdPartials, newPageReference);
this.fetchDocData("content");
}
const urlRewrite = docIdPartials.join("/");
window.history.pushState("Docs", "Docs", urlRewrite);
this.fetchDocData("content");
if (newPageReference.hash) {
this.navigateToHash(newPageReference.hash);
}
}
}
// TODO: handle language changes to doc_id
navigateToHash(hash: String) {
// eslint-disable-next-line no-use-before-define
this.template.querySelector("doc-content").navigateToHash(hash);
}
handleLanguageEvent(event: CustomEvent) {
this.selectedLanguage = event.detail;
rewriteUrl(docIdPartials: string[], newPageReference: PageReference) {
if (!this._isStorybook) {
docIdPartials[4] = newPageReference.contentDocumentId;
let urlRewrite = docIdPartials.join("/");
if (this.pageReference.hash) {
urlRewrite = `${urlRewrite}#${newPageReference.hash}`;
}
window.history.pushState("Docs", "Docs", urlRewrite);
}
}
handleVersionEvent(event: CustomEvent) {
this.selectedVersion = event.detail;
updatePageReference(newPageReference: PageReference) {
if (
newPageReference.page &&
this.pageReference.page !== newPageReference.page
) {
this.pageReference.page = newPageReference.page;
}
if (
newPageReference.docId &&
this.pageReference.docId !== newPageReference.docId
) {
this.pageReference.docId = newPageReference.docId;
}
if (
newPageReference.deliverable &&
this.pageReference.deliverable !== newPageReference.deliverable
) {
this.pageReference.deliverable = newPageReference.deliverable;
}
if (
newPageReference.contentDocumentId &&
this.pageReference.contentDocumentId !==
newPageReference.contentDocumentId
) {
if (
newPageReference.contentDocumentId.endsWith(".htm") ||
newPageReference.contentDocumentId.includes("#")
) {
this.pageReference.contentDocumentId =
newPageReference.contentDocumentId;
} else {
this.pageReference.contentDocumentId =
newPageReference.contentDocumentId + ".htm";
}
//this.updateSelectedItem();
}
if (
newPageReference.hash &&
this.pageReference.hash !== newPageReference.hash
) {
this.pageReference.hash = newPageReference.hash;
} else {
this.pageReference.hash = "";
}
}
fetchDocData(docType: string) {
let docRestUrl = "";
let isContentDocumentIdFetch = false;
/*
TODO: use set selected item for new navigation components
updateSelectedItem() {
if (this.pageReference.contentDocumentId) {
let item = "";
if (this.pageReference.contentDocumentId.includes(".htm")) {
item = this.pageReference.contentDocumentId.replace(".htm", "");
}
if (this.pageReference.hash) {
item = `${item}-${this.pageReference.hash.replace("#", "")}`;
}
this.selectedItem = item;
}
}
*/
if (this._isStorybook) {
// TODO: We have to evaluate if we can find a better way to do component testing in Storybook.
docRestUrl = `${API_MAIN_URL}/${this.selectedContentDocumentId}/${this.selectedContentId}`;
isContentDocumentIdFetch = true;
this._isStorybook = false;
} else {
docRestUrl = this.generateCalloutURL(docType);
handleToolbarEvent(event: CustomEvent) {
if (event.detail.type === "language") {
this.selectedLanguage = this.availableLanguages.filter(
(language) => language.locale === event.detail.value
);
} else if (event.detail.type === "version") {
this.selectedVersion = this.availableVersions.filter(
(version) => version.doc_version === event.detail.value
);
}
const newPageReference = { ...this.pageReference };
newPageReference.docId = `atlas.${this.selectedLanguage[0].locale}.${this.selectedVersion[0].doc_version}.${this.pageReference.deliverable}.meta`;
const docIdPartials = window.location.pathname.split("/");
this.updatePageReference(newPageReference);
docIdPartials[2] = newPageReference.docId;
const urlRewrite = docIdPartials.join("/");
window.history.pushState("Docs", "Docs", urlRewrite);
this.fetchDocData("navigation");
}
fetchDocData(docType: String) {
const docRestUrl = this.generateCalloutURL(docType);
fetch(docRestUrl)

@@ -122,21 +315,22 @@ .then((result) => {

.then((json) => {
this.docData = json;
const docData = json;
if (docType === "navigation") {
this.toc = this.docData.toc;
this.availableLanguages = this.docData.available_languages;
this.availableVersions = this.docData.available_versions;
this.selectedLanguage = this.docData.language;
this.selectedVersion = this.docData.version;
this.pdfUrl = this.docData.pdf_url;
this.toc = this.normalizeToc(docData.toc);
this.availableLanguages = docData.available_languages;
this.availableVersions = docData.available_versions;
this.selectedLanguage = [docData.language];
this.selectedVersion = [docData.version];
this.pdfUrl = docData.pdf_url;
this.navigationLoaded = true;
if (this.pageReference.contentDocumentId === undefined) {
this.pageReference.contentDocumentId =
docData.content_document_id + ".htm";
}
this.fetchDocData("content");
} else if (docType === "content") {
this.docContent = this.docData.content;
this.deliverable = this.docData.deliverable;
let urlRewrite = window.location.pathname;
if (urlRewrite.split("/").length < 2) {
urlRewrite = `${window.location.pathname}/${this.deliverable}/${this.docData.toc[0].a_attr.href}`;
this.docContent = docData.content;
this.contentLoaded = true;
if (!this.pageReference.hash) {
window.scrollTo({ top: 0, behavior: "smooth" });
}
window.history.pushState("Docs", "Docs", urlRewrite);
this.contentLoaded = true;
}

@@ -147,17 +341,11 @@ })

// Abstract the callout url creation
generateCalloutURL(docType: string) {
let base, page, docId, deliverable, contentDocumentId;
[
base,
page,
docId,
deliverable,
contentDocumentId
] = window.location.pathname.split("/");
// Generate the callout URL for the fetch based on the docType
generateCalloutURL(docType: String) {
if (docType === "navigation") {
return API_MAIN_URL + "/" + docId;
return API_MAIN_URL + this.pageReference.docId;
} else if (docType === "content") {
return `${API_DETAIL_URL}/${deliverable}/${contentDocumentId}/${this.selectedLanguage.locale}/${this.selectedVersion.doc_version}`;
return `${API_DETAIL_URL}/${this.pageReference.deliverable}/${this.pageReference.contentDocumentId}/${this.selectedLanguage[0].locale}/${this.selectedVersion[0].doc_version}`;
}
console.log("Incorrect Doc Type");
return "";
}

@@ -169,2 +357,10 @@

}
normalizeToc(toc) {
let normalizedToc = JSON.stringify(toc).replace(/"text"/g, '"label"');
normalizedToc = normalizedToc.replace(/"id"/g, '"name"');
const jsonToc = JSON.parse(normalizedToc)[0];
jsonToc.isExpanded = true;
return jsonToc;
}
}
/* eslint-disable @lwc/lwc/no-inner-html */
import { LightningElement, api, track } from "lwc";
import Prism from "./prismjs";
import {
DocContent,
PageReference
} from "../../../../../../../typings/custom-new";
import Prism from "doc/prismjs";
/**
* @element doc-content
*/
export default class Content extends LightningElement {
_docRendered: boolean = false;
@track docContent: any;
@api isStorybook: boolean = false;
@api pageReference!: PageReference;

@@ -21,2 +22,6 @@ @api

@track docContent: DocContent = "";
_docRendered: boolean = false;
//TODO: cleanup html manipulation
insertDocHtml() {

@@ -32,5 +37,5 @@ const divEl = this.template.querySelector("div");

for (const preEl of preEls) {
const codeHTML = preEl.innerHTML;
const codeEl = document.createElement("code");
preEls.forEach((preEl) => {
let codeHTML = preEl.innerHTML;
let codeEl = document.createElement("code");
codeEl.classList.add("language-js");

@@ -41,14 +46,70 @@ codeEl.innerHTML = codeHTML;

preEl.appendChild(codeEl);
}
});
// Modify anchors to work with any domain
const anchorEls = templateEl.content.querySelectorAll("a");
anchorEls.forEach((anchorEl) => {
let href = anchorEl.href.split("/");
if (
(href[3] === this.pageReference.docId && this.isStorybook) ||
href[4] === this.pageReference.docId ||
href[6] === this.pageReference.docId
) {
let updatedURL;
switch (href.length) {
case 8:
updatedURL = href.splice(5).join("/");
break;
case 7:
updatedURL = href.splice(4).join("/");
break;
case 6:
updatedURL = href.splice(3).join("/");
break;
default:
updatedURL = href.splice(6).join("/");
break;
}
for (const anchorEl of anchorEls) {
const href = anchorEl.href.split("/");
const updatedURL = href[href.length - 1];
anchorEl.addEventListener("click", this.handleNavClick.bind(this));
anchorEl.setAttribute("href", "#");
anchorEl.setAttribute("data-id", updatedURL);
}
anchorEl.addEventListener(
"click",
// eslint-disable-next-line no-use-before-define
this.handleNavClick.bind(this)
);
anchorEl.setAttribute("href", "docs/" + updatedURL);
anchorEl.setAttribute("data-id", "docs/" + updatedURL);
} else if (href[2] === "developer.salesforce.com") {
let updatedURL = this.pageReference.domain;
if (href[3]) {
updatedURL = updatedURL + `/${href[3]}`;
}
console.log(updatedURL);
if (href[4]) {
updatedURL = updatedURL + `/${href[4]}`;
}
console.log(updatedURL);
if (href[5]) {
updatedURL = updatedURL + `/${href[5]}`;
}
console.log(updatedURL);
if (href[6]) {
updatedURL = updatedURL + `/${href[6]}`;
}
console.log(updatedURL);
anchorEl.setAttribute("href", updatedURL);
anchorEl.setAttribute("data-id", updatedURL);
}
});
// Modify image src to work with any domain
const imgEls = templateEl.content.querySelectorAll("img");
imgEls.forEach((imgEl) => {
let src = imgEl.src;
let updatedURL = src.replace(
this.pageReference.domain,
"https://developer.salesforce.com"
);
imgEl.setAttribute("src", updatedURL);
});
// We don't use any tracked field here. The challenge is that

@@ -62,3 +123,8 @@ // for security reasons you can't pass pure HTML via a class

// eslint-disable-next-line no-use-before-define
Prism.highlightAllUnder(divEl);
if (this.pageReference.hash) {
this.navigateToHash(this.pageReference.hash);
}
}

@@ -68,7 +134,23 @@

event.preventDefault();
// eslint-disable-next-line no-use-before-define
let target = event.currentTarget.dataset.id;
let page,
docId,
deliverable,
tempContentDocumentId,
contentDocumentId,
hash;
[page, docId, deliverable, tempContentDocumentId] = target.split("/");
[contentDocumentId, hash] = tempContentDocumentId.split("#");
let newPageReference = {
page: page,
docId: docId,
deliverable: deliverable,
contentDocumentId: contentDocumentId,
hash: hash
};
this.dispatchEvent(
new CustomEvent("navclick", {
detail: {
url: event.target.dataset.id,
type: "content"
pageReference: newPageReference
},

@@ -81,9 +163,24 @@ bubbles: true,

@api
public navigateToHash(hash: String) {
let splitHash = hash.split("#");
if (splitHash.length === 2) {
hash = splitHash[1];
}
const anchorEl = this.template.querySelector(`[id='${hash}']`);
if (anchorEl) {
anchorEl.scrollIntoView();
} else {
window.scrollTo({ top: 0, behavior: "smooth" });
}
}
renderedCallback() {
if (this._docRendered) return;
if (this._docRendered) {
return;
}
this.insertDocHtml();
this._docRendered = true;
}
}
import { LightningElement, api } from "lwc";
import {
AvailableLanguages,
AvailableVersions,
DocToc,
PageReference,
PdfUrl,
SelectedNavigationItem,
SelectedLanguage,
SelectedVersion
} from "../../../../../../../typings/custom-new";
/**
* @element doc-nav
*/
export default class Nav extends LightningElement {
/*
@api availableLanguages!: AvailableLanguages;
@api selectedLanguage!: SelectedLanguage;
@api availableVersions!: AvailableVersions;
@api selectedVersion!: SelectedVersion;
@api selectedNavigationItem!: SelectedNavigationItem;
@api pdfUrl!: PdfUrl;
@api toc!: DocToc;
@api pageReference!: PageReference;
I've temporarily decoupled each property from docsData
@api
set docsData(value: any) {
if (value.available_languages) {
this.availableLanguages = value.available_languages;
}
if (value.available_versions) {
this.availableVersions = value.available_versions;
}
if (value.pdf_url) {
this.pdfUrl = value.pdf_url;
}
if (value.toc) {
this.toc = value.toc;
}
handleSelected(event: CustomEvent) {
event.stopPropagation();
const newPageReference = { ...this.pageReference };
const target = event.detail.name.split("-");
newPageReference.contentDocumentId = target[0] + ".htm";
newPageReference.hash = target[1];
this.dispatchEvent(
new CustomEvent("navclick", {
detail: {
pageReference: newPageReference
},
bubbles: true,
composed: true
})
);
}
get docsData() {
return null;
}
*/
@api availableLanguages = [];
@api availableVersions = [];
@api pdfUrl: string | undefined;
@api toc = [];
}
import { LightningElement } from "lwc";
/**
* @element doc-search
*/
export default class Search extends LightningElement {}
import { LightningElement, api } from "lwc";
import {
PageReference,
SelectedNavigationItem,
DocToc
} from "../../../../../../../typings/custom-new";
/**
* @element doc-toc
*/
export default class Toc extends LightningElement {
@api toc: any;
@api toc!: DocToc;
@api selectedNavigationItem!: SelectedNavigationItem;
@api pageReference!: PageReference;
handleNavClick(event: InputEvent) {
event.preventDefault();
const newPageReference = { ...this.pageReference };
// When moving to the new navigation component
//const target = event.detail.name.split('-')
const target = event.currentTarget.dataset.id.split("-");
newPageReference.contentDocumentId = target[0] + ".htm";
newPageReference.hash = target[1];
this.dispatchEvent(
new CustomEvent("navclick", {
detail: {
url: event.target.dataset.id,
type: "navigation"
pageReference: newPageReference
},

@@ -17,0 +26,0 @@ bubbles: true,

import { LightningElement, api } from "lwc";
import {
AvailableLanguages,
AvailableVersions,
PdfUrl,
SelectedLanguage,
SelectedVersion
} from "../../../../../../../typings/custom-new";
/**
* @element doc-toolbar
*/
export default class Toolbar extends LightningElement {
@api languages: string[] = [];
@api pdfUrl: string | undefined;
@api releaseVersions: string[] = [];
// Language Selector
@api availableLanguages!: AvailableLanguages;
@api selectedLanguage!: SelectedLanguage;
// Version Selector
@api availableVersions!: AvailableVersions;
@api selectedVersion!: SelectedVersion;
// PDF
@api pdfUrl!: PdfUrl;
/*
TODO: Update language on page load
renderedCallback(){
this.updateSelectedLanguage();
}
updateSelectedLanguage(){
const locale = this.selectedLanguage.locale;
const optionEl = this.template.querySelector(`option[name='${locale}']`);
console.log(JSON.stringify(optionEl));
}
*/
handleLanguageChange() {
const languageEl = this.template.querySelector("select");
const languageValue = languageEl[languageEl.selectedIndex].value;
this.dispatchEvent(
new CustomEvent("languageselected", {
detail: {
version: languageValue
},
bubbles: true,
composed: true
})
);
const languageEl = this.template.querySelector(
"select[name=languages]"
) as HTMLSelectElement;
if (languageEl) {
const languageValue = (languageEl[
languageEl.selectedIndex
] as HTMLOptionElement).value;
this.dispatchEvent(
new CustomEvent("languageselected", {
detail: {
value: languageValue,
type: "language"
},
bubbles: true,
composed: true
})
);
}
}
handleVersionChange() {
const versionEl: HTMLElement | null = this.template.querySelector(
"select"
);
const versionValue = versionEl[versionEl.selectedIndex].value;
this.dispatchEvent(
new CustomEvent("versionselected", {
detail: {
version: versionValue
},
bubbles: true,
composed: true
})
);
const versionEl = this.template.querySelector(
"select[name=versions]"
) as HTMLSelectElement;
if (versionEl) {
const versionValue = (versionEl[
versionEl.selectedIndex
] as HTMLOptionElement).value;
this.dispatchEvent(
new CustomEvent("versionselected", {
detail: {
value: versionValue,
type: "version"
},
bubbles: true,
composed: true
})
);
}
}
}

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc