Skip to content

Commit

Permalink
language switcher (#146)
Browse files Browse the repository at this point in the history
  • Loading branch information
dmca-glasgow committed Dec 12, 2023
1 parent 017d12f commit 1a548bd
Show file tree
Hide file tree
Showing 6 changed files with 179 additions and 16 deletions.
5 changes: 5 additions & 0 deletions compiler/src/cli/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,10 @@ const args = {
type: 'string',
description: 'Specify which environment program to display',
},
envLanguage: {
type: 'string',
description: 'Specify which environment language to display',
},
fileName: {
type: 'string',
description: 'Specify name of output file',
Expand Down Expand Up @@ -116,6 +120,7 @@ const options: Options = {
verbose: argv.verbose,
envPlatform: argv.envPlatform,
envProgram: argv.envProgram,
envLanguage: argv.envLanguage,
fileName: argv.fileName,
output: argv.output as 'md' | 'html',
};
Expand Down
1 change: 1 addition & 0 deletions compiler/src/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export type Options = {
verbose?: boolean;
envPlatform?: string;
envProgram?: string;
envLanguage?: string;
fileName?: string;
output?: 'md' | 'html';
};
Expand Down
64 changes: 64 additions & 0 deletions compiler/src/mdast/__test__/language-switcher.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import {
testProcessor,
unindentString,
} from '../../test-utils/test-processor';

describe('program switcher', () => {
it('should show the program switcher correctly', async () => {
const { html } = await testProcessor(`
::::language-switcher
:::r
I am R
:::
:::python
I am Python
:::
::::
`);

const expected = unindentString(`
<div class="language-switcher">
<ul>
<li data-language="r">R</li>
<li data-language="python">Python</li>
</ul>
<div data-language="r" class="language">
<p>I am R</p>
</div>
<div data-language="python" class="language">
<p>I am Python</p>
</div>
</div>
`);

expect(html).toBe(expected);
});

it('should only show cli', async () => {
const md = `
::::language-switcher
:::r
I am R
:::
:::python
I am Python
:::
::::
`;
const { html } = await testProcessor(md, {
envLanguage: 'r',
});

const expected = unindentString(`
<div class="language-switcher">
<div data-language="r" class="language show">
<p>I am R</p>
</div>
</div>
`);

expect(html).toBe(expected);
});
});
22 changes: 7 additions & 15 deletions compiler/src/mdast/__test__/program-switcher.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,17 @@ import {

describe('program switcher', () => {
it('should show the program switcher correctly', async () => {
const { html } = await testProcessor(
`
const { html } = await testProcessor(`
::::program-switcher
:::command-line
I am cli
:::
:::github-desktop
I am github desktop
:::
::::
`
);
`);

const expected = unindentString(`
<div class="program-switcher">
Expand All @@ -40,24 +36,20 @@ describe('program switcher', () => {
});

it('should only show cli', async () => {
const { html } = await testProcessor(
`
const md = `
::::program-switcher
:::command-line
I am cli
:::
:::github-desktop
I am github desktop
:::
::::
`,
{
envProgram: 'command-line',
}
);
`;
const { html } = await testProcessor(md, {
envProgram: 'command-line',
});

const expected = unindentString(`
<div class="program-switcher">
Expand Down
4 changes: 3 additions & 1 deletion compiler/src/mdast/combined.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,17 @@ import { Context } from '../context';
import { boxouts } from './boxouts';
import { moveAnswersToEnd } from './move-answers-to-end';
import { programSwitcher } from './program-switcher';
import { languageSwitcher } from './language-switcher';

export async function combinedMdastPhase(
mdast: Root,
ctx: Context,
file: VFile,
targetPdf?: boolean
targetPdf?: boolean,
) {
const processor = unified()
.use(programSwitcher, ctx)
.use(languageSwitcher, ctx)
.use(boxouts, ctx.refStore);

if (targetPdf) {
Expand Down
99 changes: 99 additions & 0 deletions compiler/src/mdast/language-switcher.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { Element, ElementContent } from 'hast';
import { ContainerDirective } from 'mdast-util-directive';
import { toHast } from 'mdast-util-to-hast';
import { Node } from 'unist';
import { visit } from 'unist-util-visit';

import { Context } from '../context';

const languages = ['r', 'python'];
const titleCase = ['R', 'Python'];

export function languageSwitcher(ctx: Context) {
const languageFlag = ctx.options.envLanguage;
if (languageFlag !== undefined && !languages.includes(languageFlag)) {
throw new Error(
`[environment]: envLanguage ${languageFlag} should be one of ${languages}`,
);
}

return (tree: Node) => {
visit(tree, 'containerDirective', (node: ContainerDirective) => {
if (node.name === 'language-switcher') {
const children = [];
if (languageFlag === undefined) {
children.push(processMenu(node));
}
children.push(...processChildren(node, languageFlag));

node.data = {
hProperties: {
className: 'language-switcher',
},
hChildren: children,
};
}
});
};
}

function processMenu(parent: ContainerDirective): ElementContent {
const children = parent.children as ContainerDirective[];
return {
type: 'element',
tagName: 'ul',
properties: {},
children: children.map((node) => {
const element: ElementContent = {
type: 'element',
tagName: 'li',
properties: {
'data-language': node.name,
},
children: [
{
type: 'text',
value: titleCase[languages.indexOf(node.name)],
},
],
};
return element;
}),
};
}

function processChildren(
parent: ContainerDirective,
languageFlag: string | undefined,
): ElementContent[] {
const children = parent.children.map((node) => {
const parent = node as ContainerDirective;
if (languages.includes(parent.name)) {
node.data = {
hProperties: {
'data-language': parent.name,
className: [
'language',
languageFlag === parent.name ? 'show' : '',
],
},
};
}
return node;
});

let filtered = children;
if (languageFlag !== undefined) {
filtered = filtered.filter((node) => {
const parent = node as ContainerDirective;
return languageFlag === parent.name;
});
}

const parentHast = toHast({
type: 'root',
children: filtered,
}) as Element;

return parentHast.children;
}

0 comments on commit 1a548bd

Please sign in to comment.