Skip to content

Commit

Permalink
Add the add-foundation-component prompt (#38)
Browse files Browse the repository at this point in the history
# Pull Request

## 📖 Description

<!--- Provide some background and a description of your work. -->
This update does the following:
- Adds the `add-foundation-component` command
- Adds the "blank" template to the list of foundation components
- Updates the documentation to normalize it
- Adds the documentation for the `add-foundation-component` command

### 🎫 Issues

<!---
List and link relevant issues here using the keyword "closes"
if this PR will close an issue, eg. closes #411
-->
Partially takes care of #31

## 👩‍💻 Reviewer Notes

<!---
Provide some notes for reviewers to help them provide targeted feedback and testing.
-->
In order to not have a massive PR down the line, foundation components will be added individually as PRs. This PR only takes care of the command line option and moves the tests around to better facilitate this update.

## ✅ Checklist

### General

<!--- Review the list and put an x in the boxes that apply. -->

- [x] I have added tests for my changes.
- [x] I have tested my changes.
- [x] I have updated the project documentation to reflect my changes.

## ⏭ Next Steps

<!---
If there is relevant follow-up work to this PR, please list any existing issues or provide brief descriptions of what you would like to do next.
-->
- Add each foundation component
  • Loading branch information
janechu committed May 10, 2022
1 parent 01e3686 commit 55431ec
Show file tree
Hide file tree
Showing 12 changed files with 190 additions and 73 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "none",
"comment": "Added the add-foundation-component command",
"packageName": "@microsoft/fast-cli",
"email": "7559015+janechu@users.noreply.github.com",
"dependentChangeType": "none"
}
10 changes: 10 additions & 0 deletions packages/fast-cli/src/cli.options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,14 @@ export interface AddComponentOptions {
export interface AddComponentOptionMessages {
name: string;
template: string;
}

export interface AddFoundationComponentOptions {
name?: string;
template?: string;
}

export interface AddFoundationComponentOptionMessages {
name: string;
template: string;
}
84 changes: 42 additions & 42 deletions packages/fast-cli/src/cli.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ function setup() {
"fast:init": `fast init -t ${path.resolve(dirname, "../cfp-template")}`,
"fast:config": `fast config -p ./src/components`,
"fast:add-design-system": `fast add-design-system -p test -s open`,
"fast:add-component:blank": `fast add-component -n test-component -t blank`,
"fast:add-component:template": `fast add-component -n test-component -t ${path.resolve(dirname, "../temp-component")}`
"fast:add-component:template": `fast add-component -n test-component -t ${path.resolve(dirname, "../temp-component")}`,
"fast:add-foundation-component:blank": `fast add-foundation-component -n test-component -t blank`,
};
fs.writeFileSync(path.resolve(tempDir, "package.json"), JSON.stringify(packageJson, null, 2));
}
Expand Down Expand Up @@ -204,56 +204,56 @@ test.describe("CLI", () => {
});
});
test.describe("add-component", () => {
test.describe("template", () => {
test.beforeAll(() => {
setup();
execSync(`cd ${tempDir} && npm run fast:init`);
setup();
setupBlankAsTemplate();
execSync(`cd ${tempDir} && npm run fast:add-component:template`);
});
test.afterAll(() => {
teardown();
});
test("should copy files from a provided template", () => {
let files: Array<string> = [];
test.beforeAll(() => {
setup();
execSync(`cd ${tempDir} && npm run fast:init`);
setup();
setupBlankAsTemplate();
execSync(`cd ${tempDir} && npm run fast:add-component:template`);
});
test.afterAll(() => {
teardown();
});
test("should copy files from a provided template", () => {
let files: Array<string> = [];

function testGeneratedFiles(folderName: string) {
const tempDirContents = fs.readdirSync(path.resolve(tempDir, "src/components/test-component", folderName));
const tempDirContentsWithFileTypes = fs.readdirSync(path.resolve(tempDir, "src/components/test-component", folderName), {
withFileTypes: true
});
function testGeneratedFiles(folderName: string) {
const tempDirContents = fs.readdirSync(path.resolve(tempDir, "src/components/test-component", folderName));
const tempDirContentsWithFileTypes = fs.readdirSync(path.resolve(tempDir, "src/components/test-component", folderName), {
withFileTypes: true
});

for (let i = 0, contentLength = tempDirContents.length; i < contentLength; i++) {
if (tempDirContentsWithFileTypes[i].isDirectory()) {
testGeneratedFiles(tempDirContents[i]);
} else {
files.push(
folderName
? `${folderName}/${tempDirContents[i]}`
: tempDirContents[i]
);
}
for (let i = 0, contentLength = tempDirContents.length; i < contentLength; i++) {
if (tempDirContentsWithFileTypes[i].isDirectory()) {
testGeneratedFiles(tempDirContents[i]);
} else {
files.push(
folderName
? `${folderName}/${tempDirContents[i]}`
: tempDirContents[i]
);
}
}

testGeneratedFiles("");
expect(files).toEqual(expectedGeneratedComponentTemplateFiles);
});
test("should be able to run the build", () => {
expect(
() => {
execSync(`cd ${tempDir} && npm run build`);
}
).not.toThrow();
});
}

testGeneratedFiles("");
expect(files).toEqual(expectedGeneratedComponentTemplateFiles);
});
test("should be able to run the build", () => {
expect(
() => {
execSync(`cd ${tempDir} && npm run build`);
}
).not.toThrow();
});
});
test.describe("add-foundation-component", () => {
test.describe("blank", () => {
test.beforeAll(() => {
setup();
execSync(`cd ${tempDir} && npm run fast:init`);
setup();
execSync(`cd ${tempDir} && npm run fast:add-component:blank`);
execSync(`cd ${tempDir} && npm run fast:add-foundation-component:blank`);
});
test.afterAll(() => {
teardown();
Expand Down
89 changes: 69 additions & 20 deletions packages/fast-cli/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import * as commander from "commander";
import prompts from "prompts";
import spawn from "cross-spawn";
import fs from "fs-extra";
import type { AddComponentOptionMessages, AddComponentOptions, AddDesignSystemOptionMessages, AddDesignSystemOptions, FastConfig, FastConfigOptionMessages, FastInit, FastInitOptionMessages, PackageJsonAddComponent, PackageJsonInit } from "./cli.options.js";
import type { AddComponentOptionMessages, AddComponentOptions, AddDesignSystemOptionMessages, AddDesignSystemOptions, AddFoundationComponentOptionMessages, AddFoundationComponentOptions, FastConfig, FastConfigOptionMessages, FastInit, FastInitOptionMessages, PackageJsonAddComponent, PackageJsonInit } from "./cli.options.js";
import { requiredComponentTemplateFiles } from "./components/files.js";
import { componentTemplateFileNotFoundMessage, componentTemplateFilesNotFoundMessage, fastConfigDoesNotContainComponentPathMessage, fastConfigDoesNotExistErrorMessage } from "./cli.errors.js";
import type { XOR } from "./cli.types.js";
Expand Down Expand Up @@ -357,16 +357,25 @@ async function writeTemplateFiles(fastConfig: FastConfig, pathToTemplatePackage:
}
}

function isCliTemplate(template: string): boolean {
return availableTemplates.includes(template);
}

/**
* Add a "blank" component or component from a npm package or local template
*/
async function addComponent(
options: AddComponentOptions,
messages: AddComponentOptionMessages,
): Promise<void> {
const config: AddComponentOptions = options;

if (!options.template) {
config.template = await prompts([
{
type: "text",
name: "template",
message: messages.template
}
]).template;
}

if (!options.name) {
config.name = await prompts([
{
Expand All @@ -377,29 +386,53 @@ async function addComponent(
]).name;
}

const fastConfig: FastConfig = await getFastConfig();

await installTemplate(options.template as string);
await checkTemplateForFiles(options.template as string);
await writeTemplateFiles(fastConfig, options.template as string, false, options.name as string);
await uninstallTemplate(options.template as string);

// await installDependencies(); // TODO: investigate adding this for foundation using fast.add-component.json
}

/**
* Add a foundation component
*/
async function addFoundationComponent(
options: AddFoundationComponentOptions,
messages: AddFoundationComponentOptionMessages,
): Promise<void> {
const config: AddFoundationComponentOptions = options;

if (!options.template) {
config.template = await prompts([
{
type: "text",
type: "autocomplete",
name: "template",
message: messages.template
message: messages.template,
choices: availableTemplates.map((availableTemplate) => {
return {
title: availableTemplate
};
})
}
]).template;
}

const fastConfig: FastConfig = await getFastConfig();
const cliTemplate = isCliTemplate(options.template as string);

if (!cliTemplate) {
await installTemplate(options.template as string);
await checkTemplateForFiles(options.template as string);
if (!options.name) {
config.name = await prompts([
{
type: "text",
name: "name",
message: messages.name,
initial: config.template
}
]).name;
}

await writeTemplateFiles(fastConfig, options.template as string, cliTemplate, options.name as string);

if (!cliTemplate) {
await uninstallTemplate(options.template as string);
}
const fastConfig: FastConfig = await getFastConfig();
await writeTemplateFiles(fastConfig, options.template as string, true, options.name as string);

// await installDependencies(); // TODO: investigate adding this for foundation using fast.add-component.json
}
Expand Down Expand Up @@ -512,8 +545,8 @@ program.command("add-design-system")
});
});

const addComponentTemplateMessage = "";
const addComponentNameMessage = "";
const addComponentTemplateMessage = "The package containing a component template";
const addComponentNameMessage = "The name of the component";

program.command("add-component")
.description("Add a component")
Expand All @@ -528,6 +561,22 @@ program.command("add-component")
})
});

const addFoundationComponentTemplateMessage = "The name of the foundation component template";
const addFoundationComponentNameMessage = "The name of the component";

program.command("add-foundation-component")
.description("Add a foundation component")
.option("-t, --template <template>", addFoundationComponentTemplateMessage)
.option("-n, --name <name>", addFoundationComponentNameMessage)
.action(async (options): Promise<void> => {
await addFoundationComponent(options, {
template: addFoundationComponentTemplateMessage,
name: addFoundationComponentNameMessage,
}).catch((reason) => {
throw reason;
})
});

program.parse(process.argv);

export { ComponentTemplateConfig };
20 changes: 13 additions & 7 deletions specs/fast-cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ export const designSystem = {
## Add a component

Components are added and configured based on a design system. Templates can use either the "blank" template with the default files, or it can point to a local or remote package.
Components are added and configured based on a design system. Templates can use either a local or remote package.

```bash
$ fast add-component
Expand All @@ -120,25 +120,31 @@ $ fast add-component

Argument | Shorthand | Description | Required | Options
---------|-----------|-------------|----------|---------
`--name <name>` | `-n <name>` | The name of the functionality to be added | Yes | |
`--template <path/to/template>` | `-t <path/to/template>` | Template including HTML, CSS, and [other files](#component-templates) | No | `<path/to/template>`, "blank" |
`--template <path/to/template>` | `-t <path/to/template>` | Template including HTML, CSS, and [other files](#component-templates) | Yes | `<path/to/template>` |
`--name <name>` | `-n <name>` | The name of the component to be added | Yes | |

> Important: The user should be prompted if no design system is present when attempting to add a new component to run the command `fast add-design-system`.
> Important: During implementation the `define.ts` which will be part of a components template may require a `package.json` update to include a list of `sideEffects`.
## Add a foundation component

Components are added and configured based on a design system and rely on a `@microsoft/fast-foundation` depenency.
Foundation components are similar to adding a component except they are based on the `@microsoft/fast-foundation` templates. These are bundled with flexible styles to provide an easy method for component creation and modification.

```bash
$ fast add-foundation-component
```

Argument | Shorthand | Description | Required | Default
### Arguments

Argument | Shorthand | Description | Required | Options
---------|-----------|-------------|----------|--------
`--template <template>` | `-t <template>` | The name of the foundation element | Yes | "accordion", "anchor", "anchored-region", "avatar", "badge", "breadcrumb", "button", "calendar", "card", "checkbox", "combobox", "data-grid", "dialog", "disclosure", "divider", "flipper", "horizontal-scroll", "listbox", "menu", "number-field", "picker", "progress", "progress-ring", "radio-group", "search", "select", "skeleton", "slider", "switch", "tabs", "text-area", "text-field", "toolbar", "tooltip", "tree-view" |
`--name <name>` | `-n <name>` | The name of the component to be added | Yes | The name of the foundation template |
`--template <template>` | `-t <template>` | The name of the foundation element | Yes | "accordion", "anchor", "anchored-region", "avatar", "badge", "blank", "breadcrumb", "button", "calendar", "card", "checkbox", "combobox", "data-grid", "dialog", "disclosure", "divider", "flipper", "horizontal-scroll", "listbox", "menu", "number-field", "picker", "progress", "progress-ring", "radio-group", "search", "select", "skeleton", "slider", "switch", "tabs", "text-area", "text-field", "toolbar", "tooltip", "tree-view" |
`--name <name>` | `-n <name>` | The name of the component to be added | Yes | |

> Important: The user should be prompted if no design system is present when attempting to add a new component to run the command `fast add-design-system`.
> Important: During implementation the `define.ts` which will be part of a components template may require a `package.json` update to include a list of `sideEffects`.
## Templates

Expand Down
3 changes: 3 additions & 0 deletions website/docs/add-a-component.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Add a component

Adding a component from a template allows a user to leverage an `npm` package or a local template they have created themselves.

## Command line
```bash
$ fast add-component
```
Expand Down
6 changes: 6 additions & 0 deletions website/docs/add-a-design-system.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,21 @@

Before adding your own components, a design system file must be available. The design system will dictate web component configuration options.

## Command line

```bash
$ fast add-design-system
```

## Arguments

Argument | Shorthand | Description | Required | Default | Options
---------|-----------|-------------|----------|---------|--------
`--prefix <prefix>` | `-p <prefix>` | The web component prefix | No | `<project-name>` |
`--shadow-root-mode <mode>` | `-s <mode>` | Determine what the shadowroot mode is | No | "open" | "closed", "open"

## Generated files

Example design-system.ts contents:
```ts
export const designSystem = {
Expand Down
16 changes: 16 additions & 0 deletions website/docs/add-a-foundation-component.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Add a foundation comopnent

Foundation components are similar to adding a component except they are based on the `@microsoft/fast-foundation` templates. These are bundled with flexible styles to provide an easy method for component creation and modification.

## Command line

```bash
$ fast add-foundation-component
```

## Arguments

Argument | Shorthand | Description | Required | Default
---------|-----------|-------------|----------|--------
`--template <template>` | `-t <template>` | The name of the foundation element | Yes | "accordion", "anchor", "anchored-region", "avatar", "badge", "blank", "breadcrumb", "button", "calendar", "card", "checkbox", "combobox", "data-grid", "dialog", "disclosure", "divider", "flipper", "horizontal-scroll", "listbox", "menu", "number-field", "picker", "progress", "progress-ring", "radio-group", "search", "select", "skeleton", "slider", "switch", "tabs", "text-area", "text-field", "toolbar", "tooltip", "tree-view" |
`--name <name>` | `-n <name>` | The name of the component to be added | Yes | The name of the foundation template |
7 changes: 5 additions & 2 deletions website/docs/configure.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,21 @@

Running the `config` command will create a `fastconfig.json` file. This should be useful in existing projects which could take advantage of the CLI once it has been initialized.

## Command line

```bash
$ fast config
```

### Arguments
## Arguments

Argument | Shorthand | Description | Required | Default |
---------|-----------|-------------|----------|---------|
`--component-path <path/to/components>` | `-p <path/to/components>` | The relative path of the FAST components folder | Yes | |

### Example fastconfig.json file
## Generated files

Example fastconfig.json file:
```json
{
"componentPath": "./src/components"
Expand Down

0 comments on commit 55431ec

Please sign in to comment.