Skip to content

Commit 769c9a4

Browse files
feat(playground): add support for generating a new playground (#2994)
* Add prompts and generate vue file * Generate other files * Use variables in headers * Add readme * Add prompt with option to generate css files * Add version to prompt and modify files accordingly * Update readme * Address PR review * Add hint for component name casing * Add Angular TS option * Reformat to use advanced prompting * Refactor where markdown output comes from * Remove default value for path; add validation * Add validation for component name --------- Co-authored-by: Amanda Johnston <[email protected]>
1 parent c768b6e commit 769c9a4

16 files changed

+1014
-3
lines changed

CONTRIBUTING.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ Ionic's documentation is built using [Docusaurus](https://docusaurus.io/). The c
5454
- `components/` - styles split out into the components they target
5555
- `static/`
5656
- `demos/` - self-contained demos, optionally presented by pages via `demoUrl` YAML frontmatter
57+
- `usage/` - playgrounds that can be created by running `npm run playground:new` [(docs)](_templates/README.md#new-playground-template)
5758
- `versioned_docs/` - versions of the docs created by the docusaurus versioning command
5859
- `versioned_sidebars/` - versions of the docs sidebars created by the docusaurus versioning command
5960

_templates/README.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Hygen templates
2+
3+
The templates in this directory are intended to be used with [hygen](https://www.hygen.io/) to generate boilerplate files. Check out [the root package.json](../package.json) to see if there are any custom commands to use them (e.g. `npm run playground:new`). You can also run e.g. `hygen playground new` to use a generator.
4+
5+
Some helpful docs links for updating/creating templates:
6+
7+
- [enquirer](https://github.com/enquirer/enquirer#toggle-prompt) for building command line prompts
8+
- [inflection](https://www.hygen.io/docs/templates#helpers-and-inflections) and [change case](https://www.hygen.io/docs/templates#change-case-helpers) for e.g. changing the case of variables submitted via the prompts
9+
10+
# New playground template
11+
12+
## Generation
13+
14+
To create a new playground, run `npm run playground:new`. This will walk you through some prompts to decide what files for the generator to create for the playground, and what their paths should be.
15+
16+
The path defaults to `basic`. If there is already a basic playground, you'll want to input a different path for the playground.
17+
18+
The CSS option will add extra files if you need to include custom CSS in your playground.
19+
20+
If you need a component for multiple versions of Ionic Framework, you (currently) need to run the generator once for each version.
21+
22+
## Usage
23+
24+
Once you've generated your playground, you need to add it to the main markdown file in the docs (e.g. [docs/api/button.md](../docs/api/button.md)) by doing something similar to the following example:
25+
26+
```
27+
## Feature
28+
29+
import Feature from '@site/static/usage/v7/button/feature/index.md';
30+
31+
<Feature />
32+
```
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
# this file's location depends on whether or not the css option or angular_ts option is selected via the prompt
3+
to: "<%= `static/usage/v${version}/${name.replace('ion-', '')}/${path}/${(css || angular_ts) ? 'angular/example_component_html.md' : 'angular.md'}` %>"
4+
---
5+
```html
6+
<<%= name %>></<%= name %>>
7+
```
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
# this file only gets generated if `css` (from the command line prompt) is true
3+
to: "<%= css ? `static/usage/v${version}/${name.replace('ion-', '')}/${path}/angular/example_component_css.md` : null %>"
4+
---
5+
```css
6+
<%= name %> {
7+
/* styles go here */
8+
}
9+
```
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
# this file only gets generated if `angular_ts` (from the command line prompt) is true
3+
to: "<%= angular_ts ? `static/usage/v${version}/${name.replace('ion-', '')}/${path}/angular/example_component_ts.md` : null %>"
4+
---
5+
```ts
6+
import { Component } from '@angular/core';
7+
8+
@Component({
9+
selector: 'app-example',
10+
templateUrl: 'example.component.html',<% if (css){ %>
11+
styleUrls: ['./example.component.css'],<% } %>
12+
})
13+
export class ExampleComponent {
14+
}
15+
```
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
---
2+
arbitrary: <% nameWithoutIon = name.replace('ion-', ''); numberOfAncestors = (path.match(/\//g) || []).length; directoryChanges = '../'.repeat(numberOfAncestors) %>
3+
to: "<%= `static/usage/v${version}/${nameWithoutIon}/${path}/demo.html` %>"
4+
---
5+
<!DOCTYPE html>
6+
<html lang="en">
7+
<head>
8+
<meta charset="UTF-8" />
9+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
10+
<title><%= h.changeCase.titleCase(nameWithoutIon) %></title>
11+
<link rel="stylesheet" href="<%= directoryChanges %>../../../common.css" />
12+
<script src="<%= directoryChanges %>../../../common.js"></script>
13+
<script type="module" src="https://cdn.jsdelivr.net/npm/@ionic/core@<%= version %>/dist/ionic/ionic.esm.js"></script>
14+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@ionic/core@<%= version %>/css/ionic.bundle.css" /><% if (css){ %>
15+
16+
<style>
17+
<%= name %> {
18+
/* styles go here */
19+
}
20+
</style><% } %>
21+
</head>
22+
23+
<body>
24+
<ion-app>
25+
<ion-content>
26+
<div class="container">
27+
<<%= name %>></<%= name %>>
28+
</div>
29+
</ion-content>
30+
</ion-app>
31+
</body>
32+
</html>

_templates/playground/new/index.js

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
const changeCase = require('change-case');
2+
3+
// see types of prompts:
4+
// https://github.com/enquirer/enquirer/tree/master/examples
5+
//
6+
module.exports = {
7+
prompt: ({ inquirer }) => {
8+
const questions = [
9+
{
10+
type: 'input',
11+
name: 'name',
12+
message: 'Which component is this playground for?',
13+
initial: 'ion-button',
14+
validate(value) {
15+
return value.match(/^ion-[a-z/-]*[a-z]+$/) ? true : "Component name must be kebab-case and begin with 'ion-'";
16+
},
17+
},
18+
{
19+
type: 'input',
20+
name: 'path',
21+
message: 'What should the playground path be?',
22+
hint: 'e.g. `basic` or `theming/colors`',
23+
validate(value) {
24+
return value.match(/^[a-z]+[a-z/-]*[a-z]+$/)
25+
? true
26+
: "Path should begin and end with a letter and only contain lowercase letters, '-', or '/'";
27+
},
28+
},
29+
{
30+
type: 'select',
31+
name: 'version',
32+
message: 'Select the Ionic Framework version for the playground',
33+
initial: '7',
34+
choices: ['6', '7'],
35+
},
36+
{
37+
type: 'toggle',
38+
name: 'css',
39+
message: 'Generate custom CSS files?',
40+
enabled: 'Yes',
41+
disabled: 'No',
42+
},
43+
{
44+
type: 'toggle',
45+
name: 'angular_ts',
46+
message: 'Generate an Angular TypeScript file?',
47+
enabled: 'Yes',
48+
disabled: 'No',
49+
},
50+
];
51+
52+
return inquirer.prompt(questions).then((answers) => {
53+
const componentName = changeCase.pascal(answers.path.split('/').pop());
54+
console.log(
55+
`\nTo use this component in a docs markdown file, include\nthe following:\n\n## ${componentName}\n\nimport ${componentName} from '@site/static/usage/v7/${answers.name.replace(
56+
'ion-',
57+
''
58+
)}/${answers.path}/index.md';\n\n<${componentName} />\n`
59+
);
60+
61+
return answers;
62+
});
63+
},
64+
};
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
arbitrary: <% nameWithoutIon = name.replace('ion-', '') %>
3+
# this template is only used if `css` (from the command line prompt) is false
4+
to: "<%= css ? null : `static/usage/v${version}/${nameWithoutIon}/${path}/index.md` %>"
5+
---
6+
import Playground from '@site/src/components/global/Playground';
7+
8+
import javascript from './javascript.md';
9+
import react from './react.md';
10+
import vue from './vue.md';
11+
import angular from './angular.md';
12+
13+
<Playground version="<%= version %>" code={{ javascript, react, vue, angular }} src="<%= `usage/v${version}/${nameWithoutIon}/${path}/demo.html` %>" />
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
---
2+
arbitrary: <% nameWithoutIon = name.replace('ion-', '') %>
3+
# this template is only used if `css` (from the command line prompt) is true
4+
to: "<%= css ? `static/usage/v${version}/${nameWithoutIon}/${path}/index.md` : null %>"
5+
---
6+
import Playground from '@site/src/components/global/Playground';
7+
8+
import javascript from './javascript.md';
9+
10+
import react_main_tsx from './react/main_tsx.md';
11+
import react_main_css from './react/main_css.md';
12+
13+
import vue from './vue.md';
14+
15+
import angular_example_component_html from './angular/example_component_html.md';
16+
import angular_example_component_css from './angular/example_component_css.md';
17+
18+
<Playground
19+
version="<%= version %>"
20+
code={{
21+
javascript,
22+
react: {
23+
files: {
24+
'src/main.tsx': react_main_tsx,
25+
'src/main.css': react_main_css,
26+
},
27+
},
28+
vue,
29+
angular: {
30+
files: {
31+
'src/app/example.component.html': angular_example_component_html,
32+
'src/app/example.component.css': angular_example_component_css,
33+
},
34+
},
35+
}}
36+
src="<%= `usage/v${version}/${nameWithoutIon}/${path}/demo.html` %>"
37+
/>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
to: "<%= `static/usage/v${version}/${name.replace('ion-', '')}/${path}/javascript.md` %>"
3+
---
4+
```html
5+
<<%= name %>></<%= name %>><% if (css){ %>
6+
7+
<style>
8+
<%= name %> {
9+
/* styles go here */
10+
}
11+
</style><% } %>
12+
```
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
---
2+
arbitrary: <% pascalName = h.changeCase.pascal(name) %>
3+
# this file's location depends on whether or not the css option is selected via the prompt
4+
to: "<%= `static/usage/v${version}/${name.replace('ion-', '')}/${path}/${css ? 'react/main_tsx.md' : 'react.md'}` %>"
5+
---
6+
```tsx
7+
import React from 'react';
8+
import { <%= pascalName %> } from '@ionic/react';<% if (css){ %>
9+
10+
import './main.css';<% } %>
11+
12+
function Example() {
13+
return (
14+
<<%= pascalName %>></<%= pascalName %>>
15+
);
16+
}
17+
export default Example;
18+
```
19+
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
# this file only gets generated if `css` (from the command line prompt) is true
3+
to: "<%= css ? `static/usage/v${version}/${name.replace('ion-', '')}/${path}/react/main_css.md` : null %>"
4+
---
5+
```css
6+
<%= name %> {
7+
/* styles go here */
8+
}
9+
```
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
---
2+
arbitrary: <% pascalName = h.changeCase.pascal(name) %>
3+
to: "<%= `static/usage/v${version}/${name.replace('ion-', '')}/${path}/vue.md` %>"
4+
---
5+
```html
6+
<template>
7+
<<%= name %>></<%= name %>>
8+
</template>
9+
10+
<script lang="ts">
11+
import { <%= pascalName %> } from '@ionic/vue';
12+
import { defineComponent } from 'vue';
13+
14+
export default defineComponent({
15+
components: {
16+
<%= pascalName %>,
17+
},
18+
});
19+
</script><% if (css){ %>
20+
21+
<style scoped>
22+
<%= name %> {
23+
/* styles go here */
24+
}
25+
</style><% } %>
26+
```

0 commit comments

Comments
 (0)