Skip to content

Commit

Permalink
feat(root): add Stackblitz functionality to component code examples
Browse files Browse the repository at this point in the history
updated code examples to work with the new Stackblitz functionality, add toggle button to switch
between typescript and javascript, add show more/less button to component code examples

. #773 #774 #751
  • Loading branch information
GCHQ-Developer-112 committed Apr 8, 2024
1 parent a749c2b commit 2bd70c7
Show file tree
Hide file tree
Showing 43 changed files with 5,058 additions and 1,279 deletions.
8 changes: 4 additions & 4 deletions src/components/CodePreview/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
justify-content: space-between;
}

.toggle-button-container {
margin: auto var(--ic-space-xs) auto 0;
}

.comp-preview {
position: relative;
border: 1px solid var(--ic-architectural-300);
Expand Down Expand Up @@ -71,12 +75,8 @@

.snippet-container {
display: flex;
justify-content: flex-end;
background-color: var(--ic-architectural-40) !important;
padding: var(--ic-space-xxs);
}

.snippet-container.pattern {
justify-content: space-between;
}

Expand Down
234 changes: 210 additions & 24 deletions src/components/CodePreview/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,40 @@ import {
} from "@mdi/js";
import Icon from "@mdi/react";
import clsx from "clsx";
import { SlottedSVG } from "@ukic/react";
import {
IcButton,
IcTab,
IcTabContext,
IcTabGroup,
IcTabPanel,
IcToggleButton,
IcToggleButtonGroup,
SlottedSVG,
} from "@ukic/react";
import StackblitzButton, {
StackblitzProps,
} from "../../content/structured/patterns/components/StackblitzButton";
import { useViewportWidth } from "../../utils/helpers";
import PageMetadataContext from "../../context/PageMetadata";
import "./index.css";
import { useViewportWidth } from "../../utils/helpers";
import {
createReactAppTsx,
createWebComponentsIndexHTML,
} from "../../content/structured/patterns/components/StackblitzButton/stackblitz-helpers";

interface Snippet {
language: string;
interface LongCodeSnippet {
language: "jsx" | "tsx";
snippet: string;
}

interface Snippet {
technology: string;
snippets: {
short?: string | undefined;
long: string | LongCodeSnippet[];
};
}

interface ComponentPreviewProps extends Partial<StackblitzProps> {
snippets?: Snippet[];
left?: boolean;
Expand All @@ -37,26 +58,35 @@ interface ComponentPreviewProps extends Partial<StackblitzProps> {
children: ReactNode;
style: CSSProperties;
state: "none" | "good" | "bad";
showStackblitzBtn: boolean;
type?: string;
}

interface CodeSnippetProps extends Partial<StackblitzProps> {
code: string;
longCode: string;
type?: string;
show: boolean;
setShow: Dispatch<SetStateAction<boolean>>;
showMore: boolean;
setShowMore: Dispatch<SetStateAction<boolean>>;
showStackblitzBtn?: boolean;
selectedLanguage: "Typescript" | "Javascript";
}

const CodeSnippet: React.FC<CodeSnippetProps> = ({
code,
longCode,
isWebComponents,
type,
show,
setShow,
showMore,
setShowMore,
showStackblitzBtn,
projectTitle,
projectDescription,
selectedLanguage,
}) => {
const viewportWidth = useViewportWidth();
const isLargeViewport: boolean = viewportWidth > 992;
Expand Down Expand Up @@ -85,9 +115,9 @@ const CodeSnippet: React.FC<CodeSnippetProps> = ({
)}
</Highlight>
)}
<div className={clsx(type === "pattern" && type, "snippet-container")}>
<div className="snippet-container">
{type === "pattern" && (
<ic-button
<IcButton
variant="tertiary"
size={isLargeViewport ? "small" : "default"}
appearance="dark"
Expand All @@ -101,18 +131,36 @@ const CodeSnippet: React.FC<CodeSnippetProps> = ({
height="24"
path={!show ? mdiMenuDown : mdiMenuUp}
/>
</ic-button>
</IcButton>
)}
{type !== "pattern" && (
<IcButton
variant="tertiary"
size={isLargeViewport ? "small" : "default"}
appearance="dark"
onClick={() => setShowMore(!showMore)}
>
Show {showMore ? "less" : "full"} code
<SlottedSVG
slot="right-icon"
viewBox="0 0 24 24"
width="24"
height="24"
path={showMore ? mdiMenuUp : mdiMenuDown}
/>
</IcButton>
)}
<div className="code-actions">
{showStackblitzBtn && projectTitle !== undefined && (
<StackblitzButton
codeSnippet={code}
codeSnippet={longCode}
isWebComponents={isWebComponents}
projectTitle={projectTitle}
projectDescription={projectDescription}
isJSX={selectedLanguage === "Javascript"}
/>
)}
<ic-button
<IcButton
aria-label={isLargeViewport ? "" : "Copy code"}
variant={isLargeViewport ? "tertiary" : "icon"}
size={isLargeViewport ? "small" : "default"}
Expand All @@ -132,7 +180,7 @@ const CodeSnippet: React.FC<CodeSnippetProps> = ({
height="24"
/>
{isLargeViewport && "Copy code"}
</ic-button>
</IcButton>
</div>
</div>
</>
Expand All @@ -147,24 +195,107 @@ const ComponentPreview: React.FC<ComponentPreviewProps> = ({
centered = true,
style,
state = "none",
showStackblitzBtn = true,
projectTitle,
projectDescription,
type,
}) => {
const viewportWidth = useViewportWidth();
const isLargeViewport: boolean = viewportWidth > 992;

// Show/hide functionality for pattern code previews
const [show, setShow] = useState<boolean>(type !== "pattern");
// Show more/less functionality for component code previews
const [showMore, setShowMore] = useState<boolean>(false);
const [selectedTab, setSelectedTab] = useState<"Web component" | "React">(
"Web component"
);
const [selectedLanguage, setSelectedLanguage] = useState<
"Typescript" | "Javascript"
>("Typescript");

const tabSelectCallback = (ev: CustomEvent) => {
setSelectedTab(ev.detail.tabIndex === 0 ? "Web component" : "React");
};

const pageMetadata = React.useContext(PageMetadataContext);

const getTypeOfProject = (snippet: Snippet) => {
if (type === "pattern") {
return " pattern";
}
if (snippet.language === "React") {
if (snippet.technology === "React") {
return " component";
}
return "";
};

const getCodeSnippet = (snippet: Snippet) => {
const isLongCode = showMore || type === "pattern";
const longCodeIndex = selectedLanguage === "Typescript" ? 0 : 1;
let shortCodeSnippet: string | undefined = "";
let longCode = "";

if (type !== "pattern") shortCodeSnippet = snippet.snippets.short;
const lines = shortCodeSnippet?.split("\n");
if (lines && lines.length > 0) {
for (let i = 1; i < lines?.length; i += 1) {
lines[i] = `\t${lines[i]}`;
}
}
shortCodeSnippet = lines?.join("\n");

if (selectedTab === "Web component") {
if (
!Array.isArray(snippet.snippets.long) &&
typeof snippet.snippets.long === "string"
) {
longCode = snippet.snippets.long.replace(
"{shortCode}",
`${shortCodeSnippet}`
);
}

let codeSnippet;
if (showMore) {
codeSnippet = createWebComponentsIndexHTML(longCode);
} else if (type === "pattern") {
if (typeof snippet.snippets.long === "string")
codeSnippet = createWebComponentsIndexHTML(snippet.snippets.long);
} else {
codeSnippet = snippet.snippets.short;
}

return {
longCode: createWebComponentsIndexHTML(longCode),
codeSnippet: codeSnippet || "",
};
}
if (Array.isArray(snippet.snippets.long)) {
if (
!Array.isArray(snippet.snippets.long[longCodeIndex].snippet) &&
typeof snippet.snippets.long[longCodeIndex].snippet === "string"
) {
longCode = createReactAppTsx(
snippet.snippets.long[longCodeIndex].snippet.replace(
"{shortCode}",
`${shortCodeSnippet}`
),
pageMetadata.pageTitle,
longCodeIndex === 0 ? "tsx" : "jsx"
);
}
return {
longCode,
codeSnippet: isLongCode ? longCode : snippet.snippets.short,
};
}
return {
longCode: "error",
codeSnippet: "error",
};
};

return (
<div className="comp-preview">
<h4 className="offscreen">Interactive example</h4>
Expand Down Expand Up @@ -197,37 +328,92 @@ const ComponentPreview: React.FC<ComponentPreviewProps> = ({
{children}
</div>
{snippets && (
<ic-tab-context>
<IcTabContext
onIcTabSelect={tabSelectCallback}
selectedTabIndex={selectedTab === "Web component" ? 0 : 1}
>
<div className="link-zone">
<ic-tab-group inline label="Framework code snippets">
<IcTabGroup inline label="Framework code snippets">
{snippets.map((snippet, index) => (
<ic-tab tab-position={index}>{snippet.language}</ic-tab>
<IcTab tab-position={index}>{snippet.technology}</IcTab>
))}
</ic-tab-group>
</IcTabGroup>
<div className="toggle-button-container">
{selectedTab === "React" && (
<IcToggleButtonGroup size="small">
<IcToggleButton
label="Typescript"
size="small"
variant={isLargeViewport ? "default" : "icon"}
toggleChecked={selectedLanguage === "Typescript"}
onIcToggleChecked={() =>
setSelectedLanguage(
selectedLanguage === "Typescript"
? "Javascript"
: "Typescript"
)
}
>
<SlottedSVG
slot="icon"
viewBow="0 0 24 24"
width="24"
height="24"
>
<path d="M3,3H21V21H3V3M13.71,17.86C14.21,18.84 15.22,19.59 16.8,19.59C18.4,19.59 19.6,18.76 19.6,17.23C19.6,15.82 18.79,15.19 17.35,14.57L16.93,14.39C16.2,14.08 15.89,13.87 15.89,13.37C15.89,12.96 16.2,12.64 16.7,12.64C17.18,12.64 17.5,12.85 17.79,13.37L19.1,12.5C18.55,11.54 17.77,11.17 16.7,11.17C15.19,11.17 14.22,12.13 14.22,13.4C14.22,14.78 15.03,15.43 16.25,15.95L16.67,16.13C17.45,16.47 17.91,16.68 17.91,17.26C17.91,17.74 17.46,18.09 16.76,18.09C15.93,18.09 15.45,17.66 15.09,17.06L13.71,17.86M13,11.25H8V12.75H9.5V20H11.25V12.75H13V11.25Z" />
</SlottedSVG>
</IcToggleButton>
<IcToggleButton
size="small"
label="Javascript"
variant={isLargeViewport ? "default" : "icon"}
toggleChecked={selectedLanguage === "Javascript"}
onIcToggleChecked={() =>
setSelectedLanguage(
selectedLanguage === "Typescript"
? "Javascript"
: "Typescript"
)
}
>
<SlottedSVG
slot="icon"
viewBow="0 0 24 24"
width="24"
height="24"
>
<path d="M3,3H21V21H3V3M7.73,18.04C8.13,18.89 8.92,19.59 10.27,19.59C11.77,19.59 12.8,18.79 12.8,17.04V11.26H11.1V17C11.1,17.86 10.75,18.08 10.2,18.08C9.62,18.08 9.38,17.68 9.11,17.21L7.73,18.04M13.71,17.86C14.21,18.84 15.22,19.59 16.8,19.59C18.4,19.59 19.6,18.76 19.6,17.23C19.6,15.82 18.79,15.19 17.35,14.57L16.93,14.39C16.2,14.08 15.89,13.87 15.89,13.37C15.89,12.96 16.2,12.64 16.7,12.64C17.18,12.64 17.5,12.85 17.79,13.37L19.1,12.5C18.55,11.54 17.77,11.17 16.7,11.17C15.19,11.17 14.22,12.13 14.22,13.4C14.22,14.78 15.03,15.43 16.25,15.95L16.67,16.13C17.45,16.47 17.91,16.68 17.91,17.26C17.91,17.74 17.46,18.09 16.76,18.09C15.93,18.09 15.45,17.66 15.09,17.06L13.71,17.86Z" />
</SlottedSVG>
</IcToggleButton>
</IcToggleButtonGroup>
)}
</div>
</div>
{snippets.map((snippet, index) => (
<ic-tab-panel tab-position={index}>
<IcTabPanel tab-position={index}>
<CodeSnippet
type={type}
code={snippet.snippet}
code={getCodeSnippet(snippet)?.codeSnippet}
longCode={getCodeSnippet(snippet)?.longCode}
show={show}
setShow={setShow}
showStackblitzBtn={
!!process.env.GATSBY_GA_TRACKING_ID && type === "pattern"
}
isWebComponents={snippet.language === "Web component"}
showMore={showMore}
setShowMore={setShowMore}
showStackblitzBtn={showStackblitzBtn}
isWebComponents={snippet.technology === "Web component"}
projectTitle={`${
projectTitle || startCase(pageMetadata.pageTitle)
} (${snippet.language}${getTypeOfProject(snippet)})`}
} (${snippet.technology}${getTypeOfProject(snippet)})`}
projectDescription={
projectDescription === undefined || projectDescription === ""
? undefined
: projectDescription
}
selectedLanguage={selectedLanguage}
/>
</ic-tab-panel>
</IcTabPanel>
))}
</ic-tab-context>
</IcTabContext>
)}
</div>
);
Expand Down
Loading

0 comments on commit 2bd70c7

Please sign in to comment.