Skip to content

Commit

Permalink
feat(playground): add support for generating a new playground (ionic-…
Browse files Browse the repository at this point in the history
…team#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]>
  • Loading branch information
mapsandapps and averyjohnston authored Jul 10, 2023
1 parent c768b6e commit 769c9a4
Show file tree
Hide file tree
Showing 16 changed files with 1,014 additions and 3 deletions.
1 change: 1 addition & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ Ionic's documentation is built using [Docusaurus](https://docusaurus.io/). The c
- `components/` - styles split out into the components they target
- `static/`
- `demos/` - self-contained demos, optionally presented by pages via `demoUrl` YAML frontmatter
- `usage/` - playgrounds that can be created by running `npm run playground:new` [(docs)](_templates/README.md#new-playground-template)
- `versioned_docs/` - versions of the docs created by the docusaurus versioning command
- `versioned_sidebars/` - versions of the docs sidebars created by the docusaurus versioning command

Expand Down
32 changes: 32 additions & 0 deletions _templates/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Hygen templates

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.

Some helpful docs links for updating/creating templates:

- [enquirer](https://github.com/enquirer/enquirer#toggle-prompt) for building command line prompts
- [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

# New playground template

## Generation

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.

The path defaults to `basic`. If there is already a basic playground, you'll want to input a different path for the playground.

The CSS option will add extra files if you need to include custom CSS in your playground.

If you need a component for multiple versions of Ionic Framework, you (currently) need to run the generator once for each version.

## Usage

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:

```
## Feature
import Feature from '@site/static/usage/v7/button/feature/index.md';
<Feature />
```
7 changes: 7 additions & 0 deletions _templates/playground/new/angular.md.ejs.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
# this file's location depends on whether or not the css option or angular_ts option is selected via the prompt
to: "<%= `static/usage/v${version}/${name.replace('ion-', '')}/${path}/${(css || angular_ts) ? 'angular/example_component_html.md' : 'angular.md'}` %>"
---
```html
<<%= name %>></<%= name %>>
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
# this file only gets generated if `css` (from the command line prompt) is true
to: "<%= css ? `static/usage/v${version}/${name.replace('ion-', '')}/${path}/angular/example_component_css.md` : null %>"
---
```css
<%= name %> {
/* styles go here */
}
```
15 changes: 15 additions & 0 deletions _templates/playground/new/angular_example_component_ts.md.ejs.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
# this file only gets generated if `angular_ts` (from the command line prompt) is true
to: "<%= angular_ts ? `static/usage/v${version}/${name.replace('ion-', '')}/${path}/angular/example_component_ts.md` : null %>"
---
```ts
import { Component } from '@angular/core';

@Component({
selector: 'app-example',
templateUrl: 'example.component.html',<% if (css){ %>
styleUrls: ['./example.component.css'],<% } %>
})
export class ExampleComponent {
}
```
32 changes: 32 additions & 0 deletions _templates/playground/new/demo.html.ejs.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
---
arbitrary: <% nameWithoutIon = name.replace('ion-', ''); numberOfAncestors = (path.match(/\//g) || []).length; directoryChanges = '../'.repeat(numberOfAncestors) %>
to: "<%= `static/usage/v${version}/${nameWithoutIon}/${path}/demo.html` %>"
---
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title><%= h.changeCase.titleCase(nameWithoutIon) %></title>
<link rel="stylesheet" href="<%= directoryChanges %>../../../common.css" />
<script src="<%= directoryChanges %>../../../common.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/npm/@ionic/core@<%= version %>/dist/ionic/ionic.esm.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@ionic/core@<%= version %>/css/ionic.bundle.css" /><% if (css){ %>
<style>
<%= name %> {
/* styles go here */
}
</style><% } %>
</head>

<body>
<ion-app>
<ion-content>
<div class="container">
<<%= name %>></<%= name %>>
</div>
</ion-content>
</ion-app>
</body>
</html>
64 changes: 64 additions & 0 deletions _templates/playground/new/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
const changeCase = require('change-case');

// see types of prompts:
// https://github.com/enquirer/enquirer/tree/master/examples
//
module.exports = {
prompt: ({ inquirer }) => {
const questions = [
{
type: 'input',
name: 'name',
message: 'Which component is this playground for?',
initial: 'ion-button',
validate(value) {
return value.match(/^ion-[a-z/-]*[a-z]+$/) ? true : "Component name must be kebab-case and begin with 'ion-'";
},
},
{
type: 'input',
name: 'path',
message: 'What should the playground path be?',
hint: 'e.g. `basic` or `theming/colors`',
validate(value) {
return value.match(/^[a-z]+[a-z/-]*[a-z]+$/)
? true
: "Path should begin and end with a letter and only contain lowercase letters, '-', or '/'";
},
},
{
type: 'select',
name: 'version',
message: 'Select the Ionic Framework version for the playground',
initial: '7',
choices: ['6', '7'],
},
{
type: 'toggle',
name: 'css',
message: 'Generate custom CSS files?',
enabled: 'Yes',
disabled: 'No',
},
{
type: 'toggle',
name: 'angular_ts',
message: 'Generate an Angular TypeScript file?',
enabled: 'Yes',
disabled: 'No',
},
];

return inquirer.prompt(questions).then((answers) => {
const componentName = changeCase.pascal(answers.path.split('/').pop());
console.log(
`\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(
'ion-',
''
)}/${answers.path}/index.md';\n\n<${componentName} />\n`
);

return answers;
});
},
};
13 changes: 13 additions & 0 deletions _templates/playground/new/index.md.ejs.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
arbitrary: <% nameWithoutIon = name.replace('ion-', '') %>
# this template is only used if `css` (from the command line prompt) is false
to: "<%= css ? null : `static/usage/v${version}/${nameWithoutIon}/${path}/index.md` %>"
---
import Playground from '@site/src/components/global/Playground';

import javascript from './javascript.md';
import react from './react.md';
import vue from './vue.md';
import angular from './angular.md';

<Playground version="<%= version %>" code={{ javascript, react, vue, angular }} src="<%= `usage/v${version}/${nameWithoutIon}/${path}/demo.html` %>" />
37 changes: 37 additions & 0 deletions _templates/playground/new/index_with_css.md.ejs.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
---
arbitrary: <% nameWithoutIon = name.replace('ion-', '') %>
# this template is only used if `css` (from the command line prompt) is true
to: "<%= css ? `static/usage/v${version}/${nameWithoutIon}/${path}/index.md` : null %>"
---
import Playground from '@site/src/components/global/Playground';

import javascript from './javascript.md';

import react_main_tsx from './react/main_tsx.md';
import react_main_css from './react/main_css.md';

import vue from './vue.md';

import angular_example_component_html from './angular/example_component_html.md';
import angular_example_component_css from './angular/example_component_css.md';

<Playground
version="<%= version %>"
code={{
javascript,
react: {
files: {
'src/main.tsx': react_main_tsx,
'src/main.css': react_main_css,
},
},
vue,
angular: {
files: {
'src/app/example.component.html': angular_example_component_html,
'src/app/example.component.css': angular_example_component_css,
},
},
}}
src="<%= `usage/v${version}/${nameWithoutIon}/${path}/demo.html` %>"
/>
12 changes: 12 additions & 0 deletions _templates/playground/new/javascript.md.ejs.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
to: "<%= `static/usage/v${version}/${name.replace('ion-', '')}/${path}/javascript.md` %>"
---
```html
<<%= name %>></<%= name %>><% if (css){ %>
<style>
<%= name %> {
/* styles go here */
}
</style><% } %>
```
19 changes: 19 additions & 0 deletions _templates/playground/new/react.md.ejs.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
arbitrary: <% pascalName = h.changeCase.pascal(name) %>
# this file's location depends on whether or not the css option is selected via the prompt
to: "<%= `static/usage/v${version}/${name.replace('ion-', '')}/${path}/${css ? 'react/main_tsx.md' : 'react.md'}` %>"
---
```tsx
import React from 'react';
import { <%= pascalName %> } from '@ionic/react';<% if (css){ %>
import './main.css';<% } %>

function Example() {
return (
<<%= pascalName %>></<%= pascalName %>>
);
}
export default Example;
```

9 changes: 9 additions & 0 deletions _templates/playground/new/react_main_css.md.ejs.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
# this file only gets generated if `css` (from the command line prompt) is true
to: "<%= css ? `static/usage/v${version}/${name.replace('ion-', '')}/${path}/react/main_css.md` : null %>"
---
```css
<%= name %> {
/* styles go here */
}
```
26 changes: 26 additions & 0 deletions _templates/playground/new/vue.md.ejs.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
arbitrary: <% pascalName = h.changeCase.pascal(name) %>
to: "<%= `static/usage/v${version}/${name.replace('ion-', '')}/${path}/vue.md` %>"
---
```html
<template>
<<%= name %>></<%= name %>>
</template>

<script lang="ts">
import { <%= pascalName %> } from '@ionic/vue';
import { defineComponent } from 'vue';
export default defineComponent({
components: {
<%= pascalName %>,
},
});
</script><% if (css){ %>
<style scoped>
<%= name %> {
/* styles go here */
}
</style><% } %>
```
Loading

0 comments on commit 769c9a4

Please sign in to comment.