Skip to content

Component demos

TL;DR

  • Codex uses VitePress to demo components
  • Run npm run doc:dev to serve the demo site locally
  • Each component should have a demo page. Demos should cover realistic use cases and variations of props and slots
  • Demos are written in Markdown files in packages/codex-docs/component-demos then compiled to packages/codex-docs/docs/components/demos

About the Codex docs site

Codex uses VitePress to demo Vue components. Component demos should provide working demonstrations and code samples for all realistic use cases of a component, plus all variations of props and slots. Ideally, a user of the library could copy and paste a code sample into their own code to use as a starting point.

Local dev environment

To serve the VitePress site locally, run this command in the root of the Codex repository:

bash
npm run doc:dev

This will both serve the VitePress site at http://localhost:5173 and compile component usage docs. If you need to serve the VitePress site on a different port, use npm run doc:dev --port=12345. For ease of development, the site uses hot module reloading: changes to the source code are immediately reflected in the browser, without needing to reload the page.

Component page generation

Although most of the docs are simply Markdown files within packages/codex-docs/docs, the component page files in packages/codex-docs/docs/components/demos are automatically generated by vue-docgen-cli.

Component demos are written in Markdown files outside of the packages/codex-docs/docs directory (see below). vue-docgen-cli is configured to grab the demo file, add documentation that's generated from the component's .vue file (page title, description, meta info, usage docs, etc.), and place the generated file in packages/codex-docs/docs/components/demos.

Code examples

Most code examples live in their own .vue file, in an examples directory underneath the relevant component's demos directory. These files are imported in the component's Markdown file, both to demonstrate the example and to display the source code. To make these code examples widely usable, they do not use TypeScript, and don't use JavaScript features that are not available in ES6.

Because the way Vue and Codex is used in MediaWiki is different, component demos display two versions of each code example: the original (labeled "NPM") and a transformed one for use in MediaWiki (labeled "MediaWiki"). The transformation is done by an automatic build process, which places MediaWiki-targeted versions of each example file in the examples-mw directory. These files then have to be imported in the Markdown files (see the examples below).

If, for some reason, the automatic transformation process doesn't produce the right output, you can also create the MediaWiki version manually. To do this, create a new file in the examples directory (not the examples-mw directory!) and add -MW to the end of the file name, e.g. examples/Foo-MW.vue. Then import this file instead of the examples-mw file in the Markdown file.

Writing component demos

Create a new demo page

Once you have a .vue file for the component in packages/codex, you can add a demo page for that component. This takes 2 steps:

  1. Add a new directory for your component to packages/codex-docs/component-demos. This new directory should exactly match the machine name of the component. Add a file, component-name.md, to that directory.
  2. Add a link to the page in the sidebar by editing VitePress config in packages/codex-docs/docs/.vitepress/config.js:
js
// In packages/codex-docs/docs/.vitepress/config.js
module.exports = {
	themeConfig: {
		sidebar: {
			'/': [
			 	...
				{
					text: 'Components',
					items: [
						// Add more components here.
						{ text: 'Button', link: '/components/demos/button' },
						{ text: 'Radio', link: '/components/demos/radio' }
					]
				},
				...
			]
		}
	}
}

Importing components

You can import Codex components directly from the @wikimedia/codex package:

js
<script setup>
import { CdxButton } from '@wikimedia/codex';
</script>

Formatting demos

A cdx-demo-wrapper component is available in all Markdown files that provides some formatting for components demos and show code/hide code functionality. To use it, place the demo code inside the demo slot, and the code sample inside the code slot. The code can either be a Markdown code block or an imported code snippet.

TIP

Note that the whitespace before and after the code sample is required for the Markdown to compile properly.

Example using a Markdown code block:

markdown
<cdx-demo-wrapper>
<template v-slot:demo>
<cdx-button weight="quiet">Click me</cdx-button>
</template>

<template v-slot:code>

```vue-html
<cdx-button>Click me</cdx-button>
```

</template>
</cdx-demo-wrapper>

Example using an imported code snippet:

markdown
<script setup>
import RadioGroup from '@/../component-demos/radio/examples/RadioGroup.vue';
</script>

<cdx-demo-wrapper>
<template v-slot:demo>
<radio-group />
</template>

<template v-slot:code>

:::code-group

<<< @/../../component-demos/radio/examples/RadioGroup.vue [NPM]

<<< @/../../component-demos/radio/examples-mw/RadioGroup.vue [MediaWiki]

:::

</template>
</cdx-demo-wrapper>

Try to keep the code samples relevant to the library user. Remember that your code sample doesn't actually have to match the code used in the demo: for example, you might display an example component for the demo but include a simplified version of that example component to display as the code snippet.

Non-trivial code snippets should have an NPM version and a MediaWiki version. For imported code snippets, the MediaWiki version is generated automatically. Code blocks embedded in the Markdown file usually so simple that a separate MediaWiki version is not needed. If an embedded code block is not simple, consider moving it to a file and importing it.

Configurable demos

If a component has several variations depending on prop and slot input, it may benefit from a configurable demo that enables users to input different prop and slot values and see the results on the fly. This can be achieved within the Wrapper component, which can take in configuration for prop and slot controls, then provide the current values of those props and slots to the component demo via a scoped slot.

To set this up, create an array of config objects, one for each prop or slot you want to allow the user to control. The following control types are available:

  • radio: for props with several known value options. Displays a radio for each provided value option.
  • boolean: For boolean props. Displays a true/false toggle that defaults to false.
  • text: For props with string or number values. Displays a text input for the value.
  • slot: For slots. Displays a text input for the slot content.

You can set a default value for each control (required for slots), otherwise the value will default to the first option (for radio controls), false (for boolean controls), or an empty string (for text controls)

See packages/codex-docs/docs/utils/types.ts for full details on control configuration, or check out the configuration for the Button demo:

js
const controlsConfig = [
	{
		name: 'action',
		type: 'radio',
		options: [ 'default', 'progressive', 'destructive' ],
	},
	{
		name: 'type',
		type: 'radio',
		options: [ 'normal', 'primary', 'quiet' ],
	},
	{
		name: 'disabled',
		type: 'boolean'
	},
	{
		name: 'default',
		type: 'slot',
		default: 'Click me'
	}
];

Next, set up the component demo. Pass the controls config to the Wrapper component via its controlsConfig prop, then use the demo slot with propValues and slotValues bindings to wrap your component demo. propValues is an object keyed on prop name with the current value of each prop, and slotValues is an object keyed on slot name with the current content of each slot. This is how the configurable Button demo is set up:

template
<cdx-demo-wrapper :controls-config="controlsConfig">
<template v-slot:demo="{ propValues, slotValues }">
<cdx-button v-bind="propValues">{{ slotValues.default }}</cdx-button>
</template>
</cdx-demo-wrapper>

Styling demo pages

Styles for a single demo page should be placed inside a <style> block in the markdown file.

Styles for example components should be scoped to ensure they only apply to that component.