diff --git a/.vscode/launch.json b/.vscode/launch.json index 0be775ab6..3d10cba23 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -87,6 +87,22 @@ "--framework=react" ] }, + { + "type": "node", + "request": "launch", + "name": "Launch New React TS ES6", + "cwd": "${workspaceRoot}/output", + "program": "${workspaceRoot}/packages/cli/bin/execute.js", + "console": "externalTerminal", + "preLaunchTask": "build", + "outFiles": ["${workspaceFolder}/**/*.js"], + "args": [ + "new", + "reactproj", + "--framework=react", + "--type=igr-ts" + ] + }, { "type": "node", "request": "launch", diff --git a/packages/cli/lib/commands/add.ts b/packages/cli/lib/commands/add.ts index dfa6e1b9b..9409c22f5 100644 --- a/packages/cli/lib/commands/add.ts +++ b/packages/cli/lib/commands/add.ts @@ -115,11 +115,23 @@ command = { }, async addTemplate(fileName: string, template: Template, options?: AddTemplateArgs): Promise { if (!options) { - options = { - parentName: "app", - parentRoutingModulePath: "src/app/app-routing.ts", - selector: "app-" + template.id - }; + if (template.framework === "react") { + options = { + parentName: "app", + className: Util.className(fileName), + modulePath: `src/app/${Util.lowerDashed(fileName)}` + }; + + options["parentRoutingModulePath"] = template.projectType === "igr-ts" + ? "src/app/app-routes.tsx" + : "./src/routes.json"; + } else { + options = { + parentName: "app", + parentRoutingModulePath: "src/app/app-routing.ts", + selector: "app-" + template.id + }; + } } fileName = fileName.trim(); diff --git a/packages/cli/lib/commands/new.ts b/packages/cli/lib/commands/new.ts index 63408f8ae..7c0359fdd 100644 --- a/packages/cli/lib/commands/new.ts +++ b/packages/cli/lib/commands/new.ts @@ -109,7 +109,11 @@ command = { const theme = projectLib.themes[themeIndex]; - const projectTemplate = argv.template || projectLib.projectIds[0]; + const indexOfEmptyOrFirst = projectLib.projectIds.indexOf("empty") !== -1 + ? projectLib.projectIds.indexOf("empty") + : 0; + + const projectTemplate = argv.template || projectLib.projectIds[indexOfEmptyOrFirst]; Util.log(`Project Name: ${argv.name}, framework ${argv.framework}, type ${projectLib.projectType}, theme ${theme}`); const projTemplate = projectLib.getProject(projectTemplate); if (projTemplate == null) { diff --git a/packages/cli/lib/commands/start.ts b/packages/cli/lib/commands/start.ts index 1b3b9b6fc..691fd22d4 100644 --- a/packages/cli/lib/commands/start.ts +++ b/packages/cli/lib/commands/start.ts @@ -84,12 +84,16 @@ command = { break; case "react": if (port) { - // https://facebook.github.io/create-react-app/docs/advanced-configuration - // react-scripts start "--port=dafaultPort" is not a valid command for all environments. - // .env file is included and used by both igr-es6 and es6 now, - // to specify the port for all environments (Windows, Mac, etc) - process.env.PORT = `${port}`; - port = null; + if (projectType === "igr-ts") { + execSyncNpmStart(port, options); + } else { + // https://facebook.github.io/create-react-app/docs/advanced-configuration + // react-scripts start "--port=dafaultPort" is not a valid command for all environments. + // .env file is included and used by both igr-es6 and es6 now, + // to specify the port for all environments (Windows, Mac, etc) + process.env.PORT = `${port}`; + port = null; + } } /* falls through */ case "angular": diff --git a/packages/cli/lib/templates/IgniteUIForReactTemplate.ts b/packages/cli/lib/templates/IgniteUIForReactTemplate.ts index e60846e8a..461633f42 100644 --- a/packages/cli/lib/templates/IgniteUIForReactTemplate.ts +++ b/packages/cli/lib/templates/IgniteUIForReactTemplate.ts @@ -1,6 +1,16 @@ -import { AddTemplateArgs, ControlExtraConfiguration, defaultDelimiters, Template, Util } from "@igniteui/cli-core"; +import { + AddTemplateArgs, + App, + ControlExtraConfiguration, + defaultDelimiters, + FS_TOKEN, + IFileSystem, + Template, + Util +} from "@igniteui/cli-core"; import * as fs from "fs-extra"; import * as path from "path"; +import { ReactTypeScriptFileUpdate } from "../../templates/react/ReactTypeScriptFileUpdate"; export class IgniteUIForReactTemplate implements Template { public components: string[]; @@ -12,15 +22,13 @@ export class IgniteUIForReactTemplate implements Template { public description: string; public dependencies: string[] = []; public framework: string = "react"; - public projectType = "igr-es6"; + public projectType: string; public hasExtraConfiguration: boolean = false; public packages = []; public delimiters = defaultDelimiters; // non-standard template prop protected widget: string; - private configFile: string = "./src/routes.json"; - /** * Base ReactTemplate constructor * @param rootPath The template folder path. Pass in `__dirname` @@ -40,6 +48,7 @@ export class IgniteUIForReactTemplate implements Template { config["path"] = this.folderName(name); //folder name allowed spaces, any casing config["name"] = Util.nameFromPath(name); // this name should not have restrictions config["ClassName"] = Util.className(Util.nameFromPath(name)); //first letter capital, no spaces and no dashes, + config["filePrefix"] = Util.lowerDashed(Util.nameFromPath(name)); config["cliVersion"] = Util.version(); if (this.widget) { config["widget"] = this.widget; @@ -52,19 +61,53 @@ export class IgniteUIForReactTemplate implements Template { return config; } - public registerInProject(projectPath: string, name: string, options?: AddTemplateArgs) { - if (options && options.skipRoute) { + public registerInProject(projectPath: string, name: string, options?: AddTemplateArgs, defaultPath = false) { + if (!options.parentName) { return; } - const configFile = fs.readFileSync(path.join(projectPath, this.configFile), "utf8"); - const viewsArr = JSON.parse(configFile); - viewsArr.push({ - componentPath: this.getViewLink(name), - path: "/" + this.folderName(Util.nameFromPath(name)), - text: this.getToolbarLink(name) - }); - fs.writeFileSync(path.join(projectPath, this.configFile), JSON.stringify(viewsArr, null, 4)); + if (this.projectType === "igr-es6") { + this.registerJSONRoute(projectPath, name, options.parentRoutingModulePath); + return; + } + + const routeModulePath: string = options.parentRoutingModulePath; + + if (!(options && options.skipRoute) + && App.container.get(FS_TOKEN).fileExists(routeModulePath)) { + let nameFromPath = Util.nameFromPath(name); + let lowerDashed = Util.lowerDashed(nameFromPath); + let filePath = path.posix.join(projectPath, options.modulePath, `${lowerDashed}.tsx`); + const routingModule = new ReactTypeScriptFileUpdate(path.join(projectPath, routeModulePath)); + + if (defaultPath) { + routingModule.addRoute("", options.className, nameFromPath, filePath, options.routerChildren, undefined); + } + + routingModule.addRoute( + lowerDashed, + options.className, + nameFromPath, + filePath, + options.routerChildren, + undefined + ); + + if (options.hasChildren) { + nameFromPath = Util.nameFromPath(`${options.modulePath}-routes.tsx`); + lowerDashed = Util.lowerDashed(nameFromPath); + filePath = path.posix.join(projectPath, options.modulePath, nameFromPath); + + routingModule.addRoute( + lowerDashed, + options.className, + nameFromPath, + filePath, + options.routerChildren, + options.importAlias + ); + } + } } public getExtraConfiguration(): ControlExtraConfiguration[] { throw new Error("Method not implemented."); @@ -93,6 +136,19 @@ export class IgniteUIForReactTemplate implements Template { } return Util.lowerDashed(folderName); } + + protected registerJSONRoute(projectPath: string, name: string, routingModulePath: string) { + const configFile = fs.readFileSync(path.join(projectPath, routingModulePath), "utf8"); + const viewsArr = JSON.parse(configFile); + viewsArr.push({ + componentPath: this.getViewLink(name), + path: "/" + this.folderName(Util.nameFromPath(name)), + text: this.getToolbarLink(name) + }); + + fs.writeFileSync(path.join(projectPath, routingModulePath), JSON.stringify(viewsArr, null, 4)); + } + protected getViewLink(name: string): string { const filePath = "./views/" + this.folderName(name); return filePath; diff --git a/packages/cli/package.json b/packages/cli/package.json index b1cda0c4a..abd24920e 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "igniteui-cli", - "version": "13.0.2", + "version": "12.1.0-beta.4", "description": "CLI tool for creating Ignite UI projects", "keywords": [ "CLI", @@ -78,8 +78,8 @@ "all": true }, "dependencies": { - "@igniteui/angular-templates": "~17.0.1302", - "@igniteui/cli-core": "~13.0.2", + "@igniteui/angular-templates": "~17.0.1210-beta.4", + "@igniteui/cli-core": "~12.1.0-beta.4", "chalk": "^2.3.2", "fs-extra": "^3.0.1", "glob": "^7.1.2", diff --git a/packages/cli/templates/react/ReactTypeScriptFileUpdate.ts b/packages/cli/templates/react/ReactTypeScriptFileUpdate.ts new file mode 100644 index 000000000..a392c85db --- /dev/null +++ b/packages/cli/templates/react/ReactTypeScriptFileUpdate.ts @@ -0,0 +1,422 @@ +import { App, FS_TOKEN, IFileSystem, TypeScriptUtils, Util } from "@igniteui/cli-core"; +import * as ts from "typescript"; + +const DEFAULT_ROUTES_VARIABLE = "routes"; +/** + * Apply various updates to typescript files using AST + */ +export class ReactTypeScriptFileUpdate { + + protected formatOptions = { spaces: false, indentSize: 4, singleQuotes: false }; + private fileSystem: IFileSystem; + private targetSource: ts.SourceFile; + private importsMeta: { lastIndex: number, modulePaths: string[] }; + + private requestedImports: Array<{ as: string | undefined, from: string, component: string, edit: boolean }>; + + private createdStringLiterals: string[]; + + /** Create updates for a file. Use `add` methods to add transformations and `finalize` to apply and save them. */ + constructor(private targetPath: string) { + this.fileSystem = App.container.get(FS_TOKEN); + this.initState(); + } + + /** Applies accumulated transforms, saves and formats the file */ + public finalize() { + // add new import statements after visitor walks: + this.addNewFileImports(); + + TypeScriptUtils.saveFile(this.targetPath, this.targetSource); + this.formatFile(this.targetPath); + // reset state in case of further updates + this.initState(); + } + + public addRoute( + path: string, + component: string, + name: string, + filePath: string, + routerChildren: string, + importAlias: string, + routesVariable = DEFAULT_ROUTES_VARIABLE + ) { + this.addRouteModuleEntry(path, component, name, filePath, routerChildren, importAlias, routesVariable); + } + + //#region File state + + /** Initializes existing imports info, [re]sets import and `NgModule` edits */ + protected initState() { + this.targetSource = TypeScriptUtils.getFileSource(this.targetPath); + this.importsMeta = this.loadImportsMeta(); + this.requestedImports = []; + this.createdStringLiterals = []; + } + + /* load some metadata about imports */ + protected loadImportsMeta() { + const meta = { lastIndex: 0, modulePaths: [] }; + + for (let i = 0; i < this.targetSource.statements.length; i++) { + const statement = this.targetSource.statements[i]; + switch (statement.kind) { + case ts.SyntaxKind.ImportDeclaration: + const importStmt = (statement as ts.ImportDeclaration); + + if (importStmt.importClause && importStmt.importClause.namedBindings && + importStmt.importClause.namedBindings.kind !== ts.SyntaxKind.NamespaceImport) { + // don't add imports without named (e.g. `import $ from "JQuery"` or `import "./my-module.js";`) + // don't add namespace imports (`import * as fs`) as available for editing, maybe in the future + meta.modulePaths.push((importStmt.moduleSpecifier as ts.StringLiteral).text); + } + + // don't add equals imports (`import url = require("url")`) as available for editing, maybe in the future + case ts.SyntaxKind.ImportEqualsDeclaration: + meta.lastIndex = i + 1; + break; + default: + break; + } + } + + return meta; + } + + //#endregion File state + + protected addRouteModuleEntry( + path: string, + component: string, + name: string, + filePath: string, + routerChildren: string, + importAlias: string, + routesVariable = DEFAULT_ROUTES_VARIABLE + ) { + const isRouting: boolean = path.indexOf("routes") >= 0; + + if (isRouting && this.targetSource.text.indexOf(path.slice(0, -4)) > 0) { + return; + } + + if (path) { + const relativePath: string = Util.relativePath(this.targetPath, filePath, true, true); + + this.requestImport(relativePath, importAlias, component); + } + + // https://github.com/Microsoft/TypeScript/issues/14419#issuecomment-307256171 + const transformer: ts.TransformerFactory = (context: ts.TransformationContext) => + (rootNode: T) => { + // the visitor that should be used when adding routes to the main route array + const conditionalVisitor: ts.Visitor = (node: ts.Node): ts.Node => { + if (node.kind === ts.SyntaxKind.ArrayLiteralExpression) { + const newObject = this.createRouteEntry(path, component, name, routerChildren); + const array = (node as ts.ArrayLiteralExpression); + this.createdStringLiterals.push(path, name); + const notFoundWildCard = ".*"; + const nodes = ts.visitNodes(array.elements, visitor); + const errorRouteNode = nodes.filter(element => element.getText().includes(notFoundWildCard))[0]; + let resultNodes = null; + if (errorRouteNode) { + resultNodes = nodes + .slice(0, nodes.indexOf(errorRouteNode)) + .concat(newObject) + .concat(errorRouteNode); + } else { + resultNodes = nodes + .concat(newObject); + } + + const elements = ts.factory.createNodeArray([ + ...resultNodes + ]); + + return ts.factory.updateArrayLiteralExpression(array, elements); + } else { + return ts.visitEachChild(node, conditionalVisitor, context); + } + }; + + let visitCondition; + + if (!isRouting) { + visitCondition = (node: ts.Node): boolean => { + return node.kind === ts.SyntaxKind.VariableDeclaration && + (node as ts.VariableDeclaration).name.getText() === routesVariable; + // no type currently + //(node as ts.VariableDeclaration).type.getText() === "Route[]"; + }; + } else { + visitCondition = (node: ts.Node): boolean => { + return undefined; + }; + } + + const visitor: ts.Visitor = this.createVisitor(conditionalVisitor, visitCondition, context); + context.enableSubstitution(ts.SyntaxKind.ClassDeclaration); + return ts.visitNode(rootNode, visitor); + }; + + this.targetSource = ts.transform(this.targetSource, [transformer], { + pretty: true // oh well.. + }).transformed[0] as ts.SourceFile; + + this.finalize(); + } + + protected requestImport(modulePath: string, routerAlias: string, componentName: string) { + const existing = this.requestedImports.find(x => x.from === modulePath); + if (!existing) { + // new imports, check if already exists in file + this.requestedImports.push({ + as: routerAlias, + from: modulePath, + component: componentName, + edit: this.importsMeta.modulePaths.indexOf(modulePath) !== -1 + }); + } else { + return; + } + } + + /** Add `import` statements not previously found in the file */ + protected addNewFileImports() { + const newImports = this.requestedImports.filter(x => !x.edit); + if (!newImports.length) { + return; + } + + const newStatements = ts.factory.createNodeArray([ + ...this.targetSource.statements.slice(0, this.importsMeta.lastIndex), + ...newImports.map(x => this.createIdentifierImport(x.from, x.as, x.component)), + ...this.targetSource.statements.slice(this.importsMeta.lastIndex) + ]); + newImports.forEach(x => this.createdStringLiterals.push(x.from)); + + this.targetSource = ts.factory.updateSourceFile(this.targetSource, newStatements); + } + + protected createIdentifierImport(importPath: string, as: string, component: string): ts.ImportDeclaration { + let exportedObject: string | undefined; + let exportedObjectName: string | undefined; + let importClause: ts.ImportClause | undefined; + if (as) { + exportedObject = "routes"; + exportedObjectName = as.replace(/\s/g, ""); + importClause = ts.factory.createImportClause( + false, + undefined, + ts.factory.createNamedImports([ + ts.factory.createImportSpecifier(false, ts.factory.createIdentifier(exportedObject), + ts.factory.createIdentifier(exportedObjectName)) + ]) + ); + } else { + importClause = ts.factory.createImportClause( + false, + ts.factory.createIdentifier(component), + undefined + ); + } + const importDeclaration = ts.factory.createImportDeclaration( + undefined, + undefined, + importClause, + ts.factory.createStringLiteral(importPath, true)); + return importDeclaration; + } + + //#region ts.TransformerFactory + + /** Transformation to apply edits to existing named import declarations */ + protected importsTransformer: ts.TransformerFactory = + (context: ts.TransformationContext) => (rootNode: T) => { + const editImports = this.requestedImports.filter(x => x.edit); + + // https://github.com/Microsoft/TypeScript/issues/14419#issuecomment-307256171 + const visitor = (node: ts.Node): ts.Node => { + if (node.kind === ts.SyntaxKind.ImportDeclaration && + editImports.find(x => x.from === ((node as ts.ImportDeclaration).moduleSpecifier as ts.StringLiteral).text) + ) { + // visit just the source file main array (second visit) + return visitImport(node as ts.ImportDeclaration); + } else { + node = ts.visitEachChild(node, visitor, context); + } + return node; + }; + function visitImport(node: ts.Node) { + node = ts.visitEachChild(node, visitImport, context); + return node; + } + return ts.visitNode(rootNode, visitor); + } + + //#endregion ts.TransformerFactory + + //#region Formatting + + /** Format a TS source file, very TBD */ + protected formatFile(filePath: string) { + // formatting via LanguageService https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API + // https://github.com/Microsoft/TypeScript/issues/1651 + + let text = this.fileSystem.readFile(filePath); + // create the language service files + const services = ts.createLanguageService(this.getLanguageHost(filePath), ts.createDocumentRegistry()); + + this.readFormatConfigs(); + const textChanges = services.getFormattingEditsForDocument(filePath, this.getFormattingOptions()); + text = this.applyChanges(text, textChanges); + + if (this.formatOptions.singleQuotes) { + for (const str of this.createdStringLiterals) { + // there shouldn't be duplicate strings of these + text = text.replace(`"${str}"`, `'${str}'`); + } + } + + this.fileSystem.writeFile(filePath, text); + } + + /** Try and parse formatting from project `.editorconfig` / `tslint.json` */ + protected readFormatConfigs() { + if (this.fileSystem.fileExists(".editorconfig")) { + // very basic parsing support + const text = this.fileSystem.readFile(".editorconfig", "utf-8"); + const options = text + .replace(/\s*[#;].*([\r\n])/g, "$1") //remove comments + .replace(/\[(?!\*\]|\*.ts).+\][^\[]+/g, "") // leave [*]/[*.ts] sections + .split(/\r\n|\r|\n/) + .reduce((obj, x) => { + if (x.indexOf("=") !== -1) { + const pair = x.split("="); + obj[pair[0].trim()] = pair[1].trim(); + } + return obj; + }, {}); + + this.formatOptions.spaces = options["indent_style"] === "space"; + if (options["indent_size"]) { + this.formatOptions.indentSize = parseInt(options["indent_size"], 10) || this.formatOptions.indentSize; + } + + if (options["quote_type"]) { + this.formatOptions.singleQuotes = options["quote_type"] === "single"; + } + } + if (this.fileSystem.fileExists("tslint.json")) { + // tslint prio - overrides other settings + const options = JSON.parse(this.fileSystem.readFile("tslint.json", "utf-8")); + if (options.rules && options.rules.indent && options.rules.indent[0]) { + this.formatOptions.spaces = options.rules.indent[1] === "spaces"; + if (options.rules.indent[2]) { + this.formatOptions.indentSize = parseInt(options.rules.indent[2], 10); + } + } + if (options.rules && options.rules.quotemark && options.rules.quotemark[0]) { + this.formatOptions.singleQuotes = options.rules.quotemark.indexOf("single") !== -1; + } + } + } + + /** + * Apply formatting changes (position based) in reverse + * from https://github.com/Microsoft/TypeScript/issues/1651#issuecomment-69877863 + */ + private applyChanges(orig: string, changes: ts.TextChange[]): string { + let result = orig; + for (let i = changes.length - 1; i >= 0; i--) { + const change = changes[i]; + const head = result.slice(0, change.span.start); + const tail = result.slice(change.span.start + change.span.length); + result = head + change.newText + tail; + } + return result; + } + + /** Return source file formatting options */ + private getFormattingOptions(): ts.FormatCodeSettings { + const formatOptions: ts.FormatCodeSettings = { + // tslint:disable:object-literal-sort-keys + indentSize: this.formatOptions.indentSize, + tabSize: 4, + newLineCharacter: ts.sys.newLine, + convertTabsToSpaces: this.formatOptions.spaces, + indentStyle: ts.IndentStyle.Smart, + insertSpaceAfterCommaDelimiter: true, + insertSpaceAfterSemicolonInForStatements: true, + insertSpaceBeforeAndAfterBinaryOperators: true, + insertSpaceAfterKeywordsInControlFlowStatements: true, + insertSpaceAfterTypeAssertion: true + // tslint:enable:object-literal-sort-keys + }; + + return formatOptions; + } + + /** Get language service host, sloppily */ + private getLanguageHost(filePath: string): ts.LanguageServiceHost { + const files = {}; + files[filePath] = { version: 0 }; + // create the language service host to allow the LS to communicate with the host + const servicesHost: ts.LanguageServiceHost = { + getCompilationSettings: () => ({}), + getScriptFileNames: () => Object.keys(files), + getScriptVersion: fileName => files[fileName] && files[fileName].version.toString(), + getScriptSnapshot: fileName => { + if (!this.fileSystem.fileExists(fileName)) { + return undefined; + } + return ts.ScriptSnapshot.fromString(this.fileSystem.readFile(fileName)); + }, + getCurrentDirectory: () => process.cwd(), + getDefaultLibFileName: options => ts.getDefaultLibFilePath(options), + readDirectory: ts.sys.readDirectory, + readFile: ts.sys.readFile, + fileExists: ts.sys.fileExists + }; + return servicesHost; + } + + //#endregion Formatting + + private createVisitor( + conditionalVisitor: ts.Visitor, + visitCondition: (node: ts.Node) => boolean, + nodeContext: ts.TransformationContext + ): ts.Visitor { + return function visitor(node: ts.Node): ts.Node { + if (visitCondition(node)) { + node = ts.visitEachChild(node, conditionalVisitor, nodeContext); + } else { + node = ts.visitEachChild(node, visitor, nodeContext); + } + return node; + }; + } + + private createRouteEntry( + path: string, + component: string, + name: string, + routerAlias: string + ): ts.ObjectLiteralExpression { + const routePath = ts.factory.createPropertyAssignment("path", ts.factory.createStringLiteral(path, true)); + const jsxElement = ts.factory.createJsxSelfClosingElement( + ts.factory.createIdentifier(component), [], undefined + ); + const routeComponent = + ts.factory.createPropertyAssignment("element", jsxElement); + const routeData = ts.factory.createPropertyAssignment("text", ts.factory.createStringLiteral(name, true)); + if (routerAlias) { + const childrenData = ts.factory.createPropertyAssignment("children", ts.factory.createIdentifier(routerAlias)); + return ts.factory.createObjectLiteralExpression([routePath, routeComponent, routeData, childrenData]); + } else { + return ts.factory.createObjectLiteralExpression([routePath, routeComponent, routeData]); + } + } +} diff --git a/packages/cli/templates/react/igr-es6/index.ts b/packages/cli/templates/react/igr-es6/index.ts index 471499607..a4c23d568 100644 --- a/packages/cli/templates/react/igr-es6/index.ts +++ b/packages/cli/templates/react/igr-es6/index.ts @@ -3,7 +3,7 @@ import { BaseProjectLibrary } from "@igniteui/cli-core"; class IgrReactProjectLibrary extends BaseProjectLibrary { constructor() { super(__dirname); - this.name = "Ignite UI for React"; + this.name = "Ignite UI for React (deprecated)"; this.projectType = "igr-es6"; this.themes = ["default"]; diff --git a/packages/cli/templates/react/igr-ts/bullet-graph/default/files/src/app/__path__/__filePrefix__.test.tsx b/packages/cli/templates/react/igr-ts/bullet-graph/default/files/src/app/__path__/__filePrefix__.test.tsx new file mode 100644 index 000000000..d8d91aeb3 --- /dev/null +++ b/packages/cli/templates/react/igr-ts/bullet-graph/default/files/src/app/__path__/__filePrefix__.test.tsx @@ -0,0 +1,8 @@ +import { expect, test } from 'vitest'; +import { render } from '@testing-library/react'; +import $(ClassName) from './index'; + +test('renders $(ClassName) component', () => { + const wrapper = render(<$(ClassName) />); + expect(wrapper).toBeTruthy(); +}); diff --git a/packages/cli/templates/react/igr-ts/bullet-graph/default/files/src/app/__path__/__filePrefix__.tsx b/packages/cli/templates/react/igr-ts/bullet-graph/default/files/src/app/__path__/__filePrefix__.tsx new file mode 100644 index 000000000..e85d14f08 --- /dev/null +++ b/packages/cli/templates/react/igr-ts/bullet-graph/default/files/src/app/__path__/__filePrefix__.tsx @@ -0,0 +1,56 @@ +import { IgrBulletGraphModule } from 'igniteui-react-gauges'; +import { IgrBulletGraph } from 'igniteui-react-gauges'; +import style from './style.module.css'; + +IgrBulletGraphModule.register(); + + +export default function $(ClassName)() { + const title = 'Bullet Graph'; + + return ( +
+

{title}

+
+ Read more on the  + + official documentation page + +
+
+
+ + +
+
+ + +
+
+
+ ) +} diff --git a/packages/cli/templates/react/igr-ts/bullet-graph/default/files/src/app/__path__/style.module.css b/packages/cli/templates/react/igr-ts/bullet-graph/default/files/src/app/__path__/style.module.css new file mode 100644 index 000000000..b79b2ac27 --- /dev/null +++ b/packages/cli/templates/react/igr-ts/bullet-graph/default/files/src/app/__path__/style.module.css @@ -0,0 +1,12 @@ +:local(.container) { + padding-top: 24px; + display: flex; + flex-flow: row; + justify-content: space-around; +} +:local(.title) { + color: rgb(0, 153, 255); +} +:local(.graph) { + width: 50%; +} diff --git a/packages/cli/templates/react/igr-ts/bullet-graph/default/index.ts b/packages/cli/templates/react/igr-ts/bullet-graph/default/index.ts new file mode 100644 index 000000000..04f521723 --- /dev/null +++ b/packages/cli/templates/react/igr-ts/bullet-graph/default/index.ts @@ -0,0 +1,17 @@ +import { IgniteUIForReactTemplate } from "../../../../../lib/templates/IgniteUIForReactTemplate"; + +class IgrTsBulletGraphTemplate extends IgniteUIForReactTemplate { + constructor() { + super(__dirname); + this.components = ["Bullet Graph"]; + this.controlGroup = "Gauges"; + this.listInComponentTemplates = true; + this.id = "bullet-graph"; + this.projectType = "igr-ts"; + this.name = "Bullet Graph"; + this.description = `allows for a linear and concise view of measures compared against a scale.`; + // TODO: read version from igniteui-react-core in package.json + this.packages = ["igniteui-react-gauges@18.3.0"]; + } +} +module.exports = new IgrTsBulletGraphTemplate(); diff --git a/packages/cli/templates/react/igr-ts/bullet-graph/index.ts b/packages/cli/templates/react/igr-ts/bullet-graph/index.ts new file mode 100644 index 000000000..21fd16de6 --- /dev/null +++ b/packages/cli/templates/react/igr-ts/bullet-graph/index.ts @@ -0,0 +1,15 @@ + +import { BaseComponent } from "@igniteui/cli-core"; + +class IgrTsBulletGraphComponent extends BaseComponent { + /** + * + */ + constructor() { + super(__dirname); + this.name = "Bullet Graph"; + this.group = "Gauges"; + this.description = `allows for a linear and concise view of measures compared against a scale.`; + } +} +module.exports = new IgrTsBulletGraphComponent(); diff --git a/packages/cli/templates/react/igr-ts/category-chart/default/files/src/app/__path__/__filePrefix__.test.tsx b/packages/cli/templates/react/igr-ts/category-chart/default/files/src/app/__path__/__filePrefix__.test.tsx new file mode 100644 index 000000000..d8d91aeb3 --- /dev/null +++ b/packages/cli/templates/react/igr-ts/category-chart/default/files/src/app/__path__/__filePrefix__.test.tsx @@ -0,0 +1,8 @@ +import { expect, test } from 'vitest'; +import { render } from '@testing-library/react'; +import $(ClassName) from './index'; + +test('renders $(ClassName) component', () => { + const wrapper = render(<$(ClassName) />); + expect(wrapper).toBeTruthy(); +}); diff --git a/packages/cli/templates/react/igr-ts/category-chart/default/files/src/app/__path__/__filePrefix__.tsx b/packages/cli/templates/react/igr-ts/category-chart/default/files/src/app/__path__/__filePrefix__.tsx new file mode 100644 index 000000000..ae79409c0 --- /dev/null +++ b/packages/cli/templates/react/igr-ts/category-chart/default/files/src/app/__path__/__filePrefix__.tsx @@ -0,0 +1,41 @@ +import { useEffect, useState } from 'react'; +import { IgrCategoryChartModule } from 'igniteui-react-charts'; +import { IgrCategoryChart } from 'igniteui-react-charts'; +import style from './style.module.css'; + +IgrCategoryChartModule.register(); + +const data = [ + { 'CountryName': 'China', 'Pop1995': 1216, 'Pop2005': 1297, 'Pop2015': 1361, 'Pop2025': 1394 }, + { 'CountryName': 'India', 'Pop1995': 920, 'Pop2005': 1090, 'Pop2015': 1251, 'Pop2025': 1396 }, + { 'CountryName': 'United States', 'Pop1995': 266, 'Pop2005': 295, 'Pop2015': 322, 'Pop2025': 351 }, + { 'CountryName': 'Indonesia', 'Pop1995': 197, 'Pop2005': 229, 'Pop2015': 256, 'Pop2025': 277 }, + { 'CountryName': 'Brazil', 'Pop1995': 161, 'Pop2005': 186, 'Pop2015': 204, 'Pop2025': 218 } +]; + +export default function $(ClassName)() { + const title = 'Category Chart'; + const [chartData, setChartData] = useState([]); + + useEffect(() => { + setChartData(data); + }, []); + + return ( +
+

{title}

+
+ Read more on the  + + official documentation page + +
+
+ + +
+
+ ) +} diff --git a/packages/cli/templates/react/igr-ts/category-chart/default/files/src/app/__path__/style.module.css b/packages/cli/templates/react/igr-ts/category-chart/default/files/src/app/__path__/style.module.css new file mode 100644 index 000000000..d5f4be4a6 --- /dev/null +++ b/packages/cli/templates/react/igr-ts/category-chart/default/files/src/app/__path__/style.module.css @@ -0,0 +1,9 @@ +:local(.container) { + padding-top: 24px; + display: flex; + flex-flow: row; + justify-content: center; +} +:local(.title) { + color: rgb(0, 153, 255); +} diff --git a/packages/cli/templates/react/igr-ts/category-chart/default/index.ts b/packages/cli/templates/react/igr-ts/category-chart/default/index.ts new file mode 100644 index 000000000..4b72b1f60 --- /dev/null +++ b/packages/cli/templates/react/igr-ts/category-chart/default/index.ts @@ -0,0 +1,18 @@ +import { IgniteUIForReactTemplate } from "../../../../../lib/templates/IgniteUIForReactTemplate"; + +class IgrTsCategoryChartTemplate extends IgniteUIForReactTemplate { + constructor() { + super(__dirname); + this.components = ["Category Chart"]; + this.controlGroup = "Charts"; + this.listInComponentTemplates = true; + this.id = "category-chart"; + this.projectType = "igr-ts"; + this.name = "Category Chart"; + this.description = `makes visualizing category data easy. Simplifies the complexities + of the data visualization domain into manageable API`; + // TODO: read version from igniteui-react-core in package.json + this.packages = ["igniteui-react-charts@18.3.0"]; + } +} +module.exports = new IgrTsCategoryChartTemplate(); diff --git a/packages/cli/templates/react/igr-ts/category-chart/index.ts b/packages/cli/templates/react/igr-ts/category-chart/index.ts new file mode 100644 index 000000000..d2ba5ea08 --- /dev/null +++ b/packages/cli/templates/react/igr-ts/category-chart/index.ts @@ -0,0 +1,16 @@ + +import { BaseComponent } from "@igniteui/cli-core"; + +class IgrTsCategoryChartComponent extends BaseComponent { + /** + * + */ + constructor() { + super(__dirname); + this.name = "Category Chart"; + this.group = "Charts"; + this.description = `makes visualizing category data easy. Simplifies the complexities + of the data visualization domain into manageable API`; + } +} +module.exports = new IgrTsCategoryChartComponent(); diff --git a/packages/cli/templates/react/igr-ts/doughnut-chart/default/files/src/app/__path__/__filePrefix__.test.tsx b/packages/cli/templates/react/igr-ts/doughnut-chart/default/files/src/app/__path__/__filePrefix__.test.tsx new file mode 100644 index 000000000..d8d91aeb3 --- /dev/null +++ b/packages/cli/templates/react/igr-ts/doughnut-chart/default/files/src/app/__path__/__filePrefix__.test.tsx @@ -0,0 +1,8 @@ +import { expect, test } from 'vitest'; +import { render } from '@testing-library/react'; +import $(ClassName) from './index'; + +test('renders $(ClassName) component', () => { + const wrapper = render(<$(ClassName) />); + expect(wrapper).toBeTruthy(); +}); diff --git a/packages/cli/templates/react/igr-ts/doughnut-chart/default/files/src/app/__path__/__filePrefix__.tsx b/packages/cli/templates/react/igr-ts/doughnut-chart/default/files/src/app/__path__/__filePrefix__.tsx new file mode 100644 index 000000000..e2943ebc9 --- /dev/null +++ b/packages/cli/templates/react/igr-ts/doughnut-chart/default/files/src/app/__path__/__filePrefix__.tsx @@ -0,0 +1,63 @@ +import { useEffect, useRef, useState } from 'react'; +import { IgrDoughnutChartModule } from 'igniteui-react-charts'; +import { IgrDoughnutChart } from 'igniteui-react-charts'; +import { IgrRingSeriesModule } from 'igniteui-react-charts'; +import { IgrRingSeries } from 'igniteui-react-charts'; +import { IgrItemLegendModule } from 'igniteui-react-charts'; +import { IgrItemLegend } from 'igniteui-react-charts'; +import style from './style.module.css'; + +IgrItemLegendModule.register(); + +IgrDoughnutChartModule.register(); +IgrRingSeriesModule.register(); + + +const data = [ + { MarketShare: 30, Company: "Google", }, + { MarketShare: 15, Company: "Microsoft", }, + { MarketShare: 30, Company: "Apple", }, + { MarketShare: 15, Company: "Samsung", }, + { MarketShare: 10, Company: "Other", }, +]; + +export default function $(ClassName)() { + const title = 'Doughnut Chart'; + const [chartData, setChartData] = useState([]); + const legendRef = useRef(); + const chartRef = useRef(); + + useEffect(() => { + setChartData(data); + }, []); + + return ( +
+

{title}

+
+ Read more on the  + + official documentation page + +
+
+
+ +
+
+ + + +
+
+
+ ) +} diff --git a/packages/cli/templates/react/igr-ts/doughnut-chart/default/files/src/app/__path__/style.module.css b/packages/cli/templates/react/igr-ts/doughnut-chart/default/files/src/app/__path__/style.module.css new file mode 100644 index 000000000..9bc1c8132 --- /dev/null +++ b/packages/cli/templates/react/igr-ts/doughnut-chart/default/files/src/app/__path__/style.module.css @@ -0,0 +1,9 @@ +:local(.container) { + display: flex; + flex-flow: row; + justify-content: center; + padding-top: 24px; +} +:local(.title) { + color: rgb(0, 153, 255); +} diff --git a/packages/cli/templates/react/igr-ts/doughnut-chart/default/index.ts b/packages/cli/templates/react/igr-ts/doughnut-chart/default/index.ts new file mode 100644 index 000000000..df639b961 --- /dev/null +++ b/packages/cli/templates/react/igr-ts/doughnut-chart/default/index.ts @@ -0,0 +1,17 @@ +import { IgniteUIForReactTemplate } from "../../../../../lib/templates/IgniteUIForReactTemplate"; + +class IgrTsDoughnutChartTemplate extends IgniteUIForReactTemplate { + constructor() { + super(__dirname); + this.components = ["Doughnut Chart"]; + this.controlGroup = "Charts"; + this.listInComponentTemplates = true; + this.id = "doughnut-chart"; + this.projectType = "igr-ts"; + this.name = "Doughnut Chart"; + this.description = `proportionally illustrate the occurrences of variables.`; + // TODO: read version from igniteui-react-core in package.json + this.packages = ["igniteui-react-charts@18.3.0"]; + } +} +module.exports = new IgrTsDoughnutChartTemplate(); diff --git a/packages/cli/templates/react/igr-ts/doughnut-chart/index.ts b/packages/cli/templates/react/igr-ts/doughnut-chart/index.ts new file mode 100644 index 000000000..ba6b1f565 --- /dev/null +++ b/packages/cli/templates/react/igr-ts/doughnut-chart/index.ts @@ -0,0 +1,14 @@ +import { BaseComponent } from "@igniteui/cli-core"; + +class IgrTsDoughnutChartComponent extends BaseComponent { + /** + * + */ + constructor() { + super(__dirname); + this.name = "Doughnut Chart"; + this.group = "Charts"; + this.description = `proportionally illustrate the occurrences of variables.`; + } +} +module.exports = new IgrTsDoughnutChartComponent(); diff --git a/packages/cli/templates/react/igr-ts/financial-chart/default/files/src/app/__path__/__filePrefix__.test.tsx b/packages/cli/templates/react/igr-ts/financial-chart/default/files/src/app/__path__/__filePrefix__.test.tsx new file mode 100644 index 000000000..d8d91aeb3 --- /dev/null +++ b/packages/cli/templates/react/igr-ts/financial-chart/default/files/src/app/__path__/__filePrefix__.test.tsx @@ -0,0 +1,8 @@ +import { expect, test } from 'vitest'; +import { render } from '@testing-library/react'; +import $(ClassName) from './index'; + +test('renders $(ClassName) component', () => { + const wrapper = render(<$(ClassName) />); + expect(wrapper).toBeTruthy(); +}); diff --git a/packages/cli/templates/react/igr-ts/financial-chart/default/files/src/app/__path__/__filePrefix__.tsx b/packages/cli/templates/react/igr-ts/financial-chart/default/files/src/app/__path__/__filePrefix__.tsx new file mode 100644 index 000000000..d0de3bf99 --- /dev/null +++ b/packages/cli/templates/react/igr-ts/financial-chart/default/files/src/app/__path__/__filePrefix__.tsx @@ -0,0 +1,49 @@ +import { useEffect, useState } from 'react' +import { IgrFinancialChartModule } from 'igniteui-react-charts'; +import { IgrFinancialChart } from 'igniteui-react-charts'; +import style from './style.module.css'; + +IgrFinancialChartModule.register(); + +const data = [ + { time: new Date(2013, 1, 1), open: 268.93, high: 268.93, low: 262.80, close: 265.00, volume: 6118146 }, + { time: new Date(2013, 1, 4), open: 262.78, high: 264.68, low: 259.07, close: 259.98, volume: 3723793 }, + { time: new Date(2013, 1, 5), open: 262.00, high: 268.03, low: 261.46, close: 266.89, volume: 4013780 }, + { time: new Date(2013, 1, 6), open: 265.16, high: 266.89, low: 261.11, close: 262.22, volume: 2772204 }, + { time: new Date(2013, 1, 7), open: 264.10, high: 264.10, low: 255.11, close: 260.23, volume: 3977065 }, + { time: new Date(2013, 1, 8), open: 261.40, high: 265.25, low: 260.56, close: 261.95, volume: 3879628 }, + { time: new Date(2013, 1, 11), open: 263.20, high: 263.25, low: 256.60, close: 257.21, volume: 3407457 }, + { time: new Date(2013, 1, 12), open: 259.19, high: 260.16, low: 257.00, close: 258.70, volume: 2944730 }, + { time: new Date(2013, 1, 13), open: 261.53, high: 269.96, low: 260.30, close: 269.47, volume: 5295786 }, + { time: new Date(2013, 1, 14), open: 267.37, high: 270.65, low: 265.40, close: 269.24, volume: 3464080 }, + { time: new Date(2013, 1, 15), open: 267.63, high: 268.92, low: 263.11, close: 265.09, volume: 3981233 } +]; + + +export default function $(ClassName)() { + const title = 'Financial Chart'; + const [chartData, setChartData] = useState([]); + + useEffect(() => { + setChartData(data); + }, []); + + return ( +
+

{title}

+
+ Read more on the  + + official documentation page + +
+
+ + +
+
+ ) +} diff --git a/packages/cli/templates/react/igr-ts/financial-chart/default/files/src/app/__path__/style.module.css b/packages/cli/templates/react/igr-ts/financial-chart/default/files/src/app/__path__/style.module.css new file mode 100644 index 000000000..2556f96e9 --- /dev/null +++ b/packages/cli/templates/react/igr-ts/financial-chart/default/files/src/app/__path__/style.module.css @@ -0,0 +1,10 @@ +:local(.container) { + padding-top: 24px; + display: flex; + flex-flow: row; + justify-content: center; + text-align: left; +} +:local(.title) { + color: rgb(0, 153, 255); +} diff --git a/packages/cli/templates/react/igr-ts/financial-chart/default/index.ts b/packages/cli/templates/react/igr-ts/financial-chart/default/index.ts new file mode 100644 index 000000000..cb5643c29 --- /dev/null +++ b/packages/cli/templates/react/igr-ts/financial-chart/default/index.ts @@ -0,0 +1,19 @@ +import { IgniteUIForReactTemplate } from "../../../../../lib/templates/IgniteUIForReactTemplate"; + +class IgrTsFinancialChartTemplate extends IgniteUIForReactTemplate { + constructor() { + super(__dirname); + this.components = ["Financial Chart"]; + this.controlGroup = "Charts"; + // set to true once bug with chart destoy is fixed + this.listInComponentTemplates = false; + this.id = "financial-chart"; + this.projectType = "igr-ts"; + this.name = "Financial Chart"; + this.description = `charting component that makes it easy to visualize financial data by + using a simple and intuitive API.`; + // TODO: read version from igniteui-react-core in package.json + this.packages = ["igniteui-react-charts@18.3.0"]; + } +} +module.exports = new IgrTsFinancialChartTemplate(); diff --git a/packages/cli/templates/react/igr-ts/financial-chart/index.ts b/packages/cli/templates/react/igr-ts/financial-chart/index.ts new file mode 100644 index 000000000..3709e026a --- /dev/null +++ b/packages/cli/templates/react/igr-ts/financial-chart/index.ts @@ -0,0 +1,15 @@ +import { BaseComponent } from "@igniteui/cli-core"; + +class IgrTsFinancialChartComponent extends BaseComponent { + /** + * + */ + constructor() { + super(__dirname); + this.name = "Financial Chart"; + this.group = "Charts"; + this.description = `charting component that makes it easy to visualize financial data by + using a simple and intuitive API.`; + } +} +module.exports = new IgrTsFinancialChartComponent(); diff --git a/packages/cli/templates/react/igr-ts/grid/basic/files/src/app/__path__/__filePrefix__.test.tsx b/packages/cli/templates/react/igr-ts/grid/basic/files/src/app/__path__/__filePrefix__.test.tsx new file mode 100644 index 000000000..d8d91aeb3 --- /dev/null +++ b/packages/cli/templates/react/igr-ts/grid/basic/files/src/app/__path__/__filePrefix__.test.tsx @@ -0,0 +1,8 @@ +import { expect, test } from 'vitest'; +import { render } from '@testing-library/react'; +import $(ClassName) from './index'; + +test('renders $(ClassName) component', () => { + const wrapper = render(<$(ClassName) />); + expect(wrapper).toBeTruthy(); +}); diff --git a/packages/cli/templates/react/igr-ts/grid/basic/files/src/app/__path__/__filePrefix__.tsx b/packages/cli/templates/react/igr-ts/grid/basic/files/src/app/__path__/__filePrefix__.tsx new file mode 100644 index 000000000..a95142f97 --- /dev/null +++ b/packages/cli/templates/react/igr-ts/grid/basic/files/src/app/__path__/__filePrefix__.tsx @@ -0,0 +1,58 @@ +import style from './style.module.css'; +import 'igniteui-react-grids/grids'; +import { IgrGridModule, IgrGrid, IgrColumn } from 'igniteui-react-grids'; +import 'igniteui-react-grids/grids/themes/light/bootstrap.css' + +import data from './data'; + +IgrGridModule.register(); + +export default function $(ClassName)() { + const title = 'Grid'; + + return ( +
+

{title}

+
+ Read more on the  + + official documentation page + +
+
+
+ + + + + + + +
+
+ +
+
+
+ ); +} diff --git a/packages/cli/templates/react/igr-ts/grid/basic/files/src/app/__path__/data.tsx b/packages/cli/templates/react/igr-ts/grid/basic/files/src/app/__path__/data.tsx new file mode 100644 index 000000000..06ba27b28 --- /dev/null +++ b/packages/cli/templates/react/igr-ts/grid/basic/files/src/app/__path__/data.tsx @@ -0,0 +1,46 @@ +export default [{ + Discontinued: false, + OrderDate: new Date('2012-02-12'), + ProductID: 1, + ProductName: 'Chai', + QuantityPerUnit: '10 boxes x 20 bags', + ReorderLevel: 10, + UnitPrice: 18.0000, + UnitsInStock: 39 +}, { + Discontinued: false, + OrderDate: new Date('2003-03-17'), + ProductID: 2, + ProductName: 'Chang', + QuantityPerUnit: '24 - 12 oz bottles', + ReorderLevel: 25, + UnitPrice: 19.0000, + UnitsInStock: 17 +}, { + Discontinued: false, + OrderDate: new Date('2006-03-17'), + ProductID: 3, + ProductName: 'Aniseed Syrup', + QuantityPerUnit: '12 - 550 ml bottles', + ReorderLevel: 25, + UnitPrice: 10.0000, + UnitsInStock: 13 +}, { + Discontinued: false, + OrderDate: new Date('2016-03-17'), + ProductID: 4, + ProductName: 'Chef Antons Cajun Seasoning', + QuantityPerUnit: '48 - 6 oz jars', + ReorderLevel: 0, + UnitPrice: 22.0000, + UnitsInStock: 53 +}, { + Discontinued: true, + OrderDate: new Date('2011-11-11'), + ProductID: 5, + ProductName: 'Chef Antons Gumbo Mix', + QuantityPerUnit: '36 boxes', + ReorderLevel: 0, + UnitPrice: 21.3500, + UnitsInStock: 0 +}]; diff --git a/packages/cli/templates/react/igr-ts/grid/basic/files/src/app/__path__/style.module.css b/packages/cli/templates/react/igr-ts/grid/basic/files/src/app/__path__/style.module.css new file mode 100644 index 000000000..4ed0c6a7b --- /dev/null +++ b/packages/cli/templates/react/igr-ts/grid/basic/files/src/app/__path__/style.module.css @@ -0,0 +1,17 @@ +:local(.container) { + padding-top: 24px; + display: flex; + flex-flow: column; + justify-content: center; + align-items: center; +} + +:local(.title) { + color: rgb(0, 153, 255); +} + +:local(.grid) { + width: 80%; + margin-bottom: 24px; + border: 1px solid rgb(0, 153, 255); +} diff --git a/packages/cli/templates/react/igr-ts/grid/basic/index.ts b/packages/cli/templates/react/igr-ts/grid/basic/index.ts new file mode 100644 index 000000000..d070bc4df --- /dev/null +++ b/packages/cli/templates/react/igr-ts/grid/basic/index.ts @@ -0,0 +1,24 @@ + +import { IgniteUIForReactTemplate } from "../../../../../lib/templates/IgniteUIForReactTemplate"; + +class GridTemplate extends IgniteUIForReactTemplate { + /** + * + */ + constructor() { + super(__dirname); + this.id = "grid"; + this.name = "Grid"; + this.widget = "igGrid"; + this.description = "IgrGrid template for React"; + this.projectType = "igr-ts"; + this.components = ["Grid"]; + this.controlGroup = "Data Grids"; + // TODO: read version from igniteui-react-core in package.json + this.packages = ["igniteui-react-grids@18.3.0", "igniteui-react-inputs@18.3.0", + "igniteui-react-layouts@18.3.0"]; + + this.hasExtraConfiguration = false; + } +} +module.exports = new GridTemplate(); diff --git a/packages/cli/templates/react/igr-ts/grid/index.ts b/packages/cli/templates/react/igr-ts/grid/index.ts new file mode 100644 index 000000000..ae88539fe --- /dev/null +++ b/packages/cli/templates/react/igr-ts/grid/index.ts @@ -0,0 +1,14 @@ +import { BaseComponent } from "@igniteui/cli-core"; + +class IgrTsDataGridComponent extends BaseComponent { + /** + * + */ + constructor() { + super(__dirname); + this.name = "Data Grid"; + this.group = "Grids & Lists"; + this.description = "pick from grids: basic, sorting, templating."; + } +} +module.exports = new IgrTsDataGridComponent(); diff --git a/packages/cli/templates/react/igr-ts/groups.json b/packages/cli/templates/react/igr-ts/groups.json new file mode 100644 index 000000000..579789370 --- /dev/null +++ b/packages/cli/templates/react/igr-ts/groups.json @@ -0,0 +1,5 @@ +{ + "Gauges": "scale measure Controls including Linear and Radial Gauge and Bullet Graph.", + "Charts": "high-performance data visualization for category and financial data.", + "Grids & Lists": "bind and display data sets with little coding or configuration." +} diff --git a/packages/cli/templates/react/igr-ts/index.ts b/packages/cli/templates/react/igr-ts/index.ts new file mode 100644 index 000000000..f76625550 --- /dev/null +++ b/packages/cli/templates/react/igr-ts/index.ts @@ -0,0 +1,17 @@ +import { BaseProjectLibrary } from "@igniteui/cli-core"; + +class IgrTsReactProjectLibrary extends BaseProjectLibrary { + constructor() { + super(__dirname); + this.name = "Ignite UI for React TS"; + this.projectType = "igr-ts"; + this.themes = ["default"]; + + const groups = require("./groups.json"); + // tslint:disable-next-line:forin + for (const key in groups) { + this.groupDescriptions.set(key, groups[key]); + } + } +} +module.exports = new IgrTsReactProjectLibrary(); diff --git a/packages/cli/templates/react/igr-ts/linear-gauge/default/files/src/app/__path__/__filePrefix__.test.tsx b/packages/cli/templates/react/igr-ts/linear-gauge/default/files/src/app/__path__/__filePrefix__.test.tsx new file mode 100644 index 000000000..d8d91aeb3 --- /dev/null +++ b/packages/cli/templates/react/igr-ts/linear-gauge/default/files/src/app/__path__/__filePrefix__.test.tsx @@ -0,0 +1,8 @@ +import { expect, test } from 'vitest'; +import { render } from '@testing-library/react'; +import $(ClassName) from './index'; + +test('renders $(ClassName) component', () => { + const wrapper = render(<$(ClassName) />); + expect(wrapper).toBeTruthy(); +}); diff --git a/packages/cli/templates/react/igr-ts/linear-gauge/default/files/src/app/__path__/__filePrefix__.tsx b/packages/cli/templates/react/igr-ts/linear-gauge/default/files/src/app/__path__/__filePrefix__.tsx new file mode 100644 index 000000000..7d4d909c5 --- /dev/null +++ b/packages/cli/templates/react/igr-ts/linear-gauge/default/files/src/app/__path__/__filePrefix__.tsx @@ -0,0 +1,59 @@ +import style from './style.module.css'; +import { IgrLinearGaugeModule } from 'igniteui-react-gauges'; +import { IgrLinearGauge } from 'igniteui-react-gauges'; + +IgrLinearGaugeModule.register(); + +export default function $(ClassName)() { + const title = 'Linear Gauge'; + return ( +
+

{title}

+
+ Read more on the  + + official documentation page + +
+
+
+ + +
+
+ + +
+
+
+ ) +} diff --git a/packages/cli/templates/react/igr-ts/linear-gauge/default/files/src/app/__path__/style.module.css b/packages/cli/templates/react/igr-ts/linear-gauge/default/files/src/app/__path__/style.module.css new file mode 100644 index 000000000..8c4e121fe --- /dev/null +++ b/packages/cli/templates/react/igr-ts/linear-gauge/default/files/src/app/__path__/style.module.css @@ -0,0 +1,12 @@ +:local(.container) { + padding-top: 24px; + display: flex; + flex-flow: row; + justify-content: space-around; +} +:local(.title) { + color: rgb(0, 153, 255); +} +:local(.gauge) { + width: 50%; +} diff --git a/packages/cli/templates/react/igr-ts/linear-gauge/default/index.ts b/packages/cli/templates/react/igr-ts/linear-gauge/default/index.ts new file mode 100644 index 000000000..3e9a11621 --- /dev/null +++ b/packages/cli/templates/react/igr-ts/linear-gauge/default/index.ts @@ -0,0 +1,17 @@ +import { IgniteUIForReactTemplate } from "../../../../../lib/templates/IgniteUIForReactTemplate"; + +class IgrTsLinearGaugeTemplate extends IgniteUIForReactTemplate { + constructor() { + super(__dirname); + this.components = ["Linear Gauge"]; + this.controlGroup = "Gauges"; + this.listInComponentTemplates = true; + this.id = "linear-gauge"; + this.projectType = "igr-ts"; + this.name = "Linear Gauge"; + this.description = `value compared against a scale and one or more ranges.`; + // TODO: read version from igniteui-react-core in package.json + this.packages = ["igniteui-react-gauges@18.3.0"]; + } +} +module.exports = new IgrTsLinearGaugeTemplate(); diff --git a/packages/cli/templates/react/igr-ts/linear-gauge/index.ts b/packages/cli/templates/react/igr-ts/linear-gauge/index.ts new file mode 100644 index 000000000..f70320e2e --- /dev/null +++ b/packages/cli/templates/react/igr-ts/linear-gauge/index.ts @@ -0,0 +1,15 @@ + +import { BaseComponent } from "@igniteui/cli-core"; + +class IgrTsLinearGaugeComponent extends BaseComponent { + /** + * + */ + constructor() { + super(__dirname); + this.name = "Linear Gauge"; + this.group = "Gauges"; + this.description = `value compared against a scale and one or more ranges.`; + } +} +module.exports = new IgrTsLinearGaugeComponent(); diff --git a/packages/cli/templates/react/igr-ts/pie-chart/default/files/src/app/__path__/__filePrefix__.test.tsx b/packages/cli/templates/react/igr-ts/pie-chart/default/files/src/app/__path__/__filePrefix__.test.tsx new file mode 100644 index 000000000..d8d91aeb3 --- /dev/null +++ b/packages/cli/templates/react/igr-ts/pie-chart/default/files/src/app/__path__/__filePrefix__.test.tsx @@ -0,0 +1,8 @@ +import { expect, test } from 'vitest'; +import { render } from '@testing-library/react'; +import $(ClassName) from './index'; + +test('renders $(ClassName) component', () => { + const wrapper = render(<$(ClassName) />); + expect(wrapper).toBeTruthy(); +}); diff --git a/packages/cli/templates/react/igr-ts/pie-chart/default/files/src/app/__path__/__filePrefix__.tsx b/packages/cli/templates/react/igr-ts/pie-chart/default/files/src/app/__path__/__filePrefix__.tsx new file mode 100644 index 000000000..9a42c847f --- /dev/null +++ b/packages/cli/templates/react/igr-ts/pie-chart/default/files/src/app/__path__/__filePrefix__.tsx @@ -0,0 +1,57 @@ +import { useEffect, useRef, useState } from 'react'; +import { IgrPieChartModule } from 'igniteui-react-charts'; +import { IgrPieChart } from 'igniteui-react-charts'; +import { IgrItemLegend } from 'igniteui-react-charts'; +import { IgrItemLegendModule } from 'igniteui-react-charts'; +import style from './style.module.css'; + +IgrPieChartModule.register(); +IgrItemLegendModule.register(); + +const data = [ + { MarketShare: 30, Company: "Google", }, + { MarketShare: 15, Company: "Microsoft", }, + { MarketShare: 30, Company: "Apple", }, + { MarketShare: 15, Company: "Samsung", }, + { MarketShare: 10, Company: "Other", }, +]; + +export default function $(ClassName)() { + const title = 'Pie Chart'; + const [chartData, setChartData] = useState([]); + let legendRef = useRef(); + let chartRef = useRef(); + + useEffect(() => { + setChartData(data); + }, []); + + return ( +
+

{title}

+
+ Read more on the  + + official documentation page + +
+
+
+ +
+
+ + +
+
+
+ ) +} diff --git a/packages/cli/templates/react/igr-ts/pie-chart/default/files/src/app/__path__/style.module.css b/packages/cli/templates/react/igr-ts/pie-chart/default/files/src/app/__path__/style.module.css new file mode 100644 index 000000000..9bc1c8132 --- /dev/null +++ b/packages/cli/templates/react/igr-ts/pie-chart/default/files/src/app/__path__/style.module.css @@ -0,0 +1,9 @@ +:local(.container) { + display: flex; + flex-flow: row; + justify-content: center; + padding-top: 24px; +} +:local(.title) { + color: rgb(0, 153, 255); +} diff --git a/packages/cli/templates/react/igr-ts/pie-chart/default/index.ts b/packages/cli/templates/react/igr-ts/pie-chart/default/index.ts new file mode 100644 index 000000000..d0a953497 --- /dev/null +++ b/packages/cli/templates/react/igr-ts/pie-chart/default/index.ts @@ -0,0 +1,17 @@ +import { IgniteUIForReactTemplate } from "../../../../../lib/templates/IgniteUIForReactTemplate"; + +class IgrTsPieChartTemplate extends IgniteUIForReactTemplate { + constructor() { + super(__dirname); + this.components = ["Pie Chart"]; + this.controlGroup = "Charts"; + this.listInComponentTemplates = true; + this.id = "pie-chart"; + this.projectType = "igr-ts"; + this.name = "Pie Chart"; + this.description = `easily illustate the proportions of data entries`; + // TODO: read version from igniteui-react-core in package.json + this.packages = ["igniteui-react-charts@18.3.0"]; + } +} +module.exports = new IgrTsPieChartTemplate(); diff --git a/packages/cli/templates/react/igr-ts/pie-chart/index.ts b/packages/cli/templates/react/igr-ts/pie-chart/index.ts new file mode 100644 index 000000000..c73c306b9 --- /dev/null +++ b/packages/cli/templates/react/igr-ts/pie-chart/index.ts @@ -0,0 +1,14 @@ +import { BaseComponent } from "@igniteui/cli-core"; + +class IgrTsPieChartComponent extends BaseComponent { + /** + * + */ + constructor() { + super(__dirname); + this.name = "Pie Chart"; + this.group = "Charts"; + this.description = `easily illustrate the proportions of data entries`; + } +} +module.exports = new IgrTsPieChartComponent(); diff --git a/packages/cli/templates/react/igr-ts/projects/_base/files/README.md b/packages/cli/templates/react/igr-ts/projects/_base/files/README.md new file mode 100644 index 000000000..d323aea07 --- /dev/null +++ b/packages/cli/templates/react/igr-ts/projects/_base/files/README.md @@ -0,0 +1,48 @@ +# $(name) + +This project was generated with [Ignite UI CLI](https://github.com/IgniteUI/igniteui-cli) version $(cliVersion).
+The template builds upon a project bootstrapped with [Vite](https://vitejs.dev/). + +## Development server + +Run `ig start` to build the application, start a web server and open the application in the default browser.
+The default serving port is `http://localhost:3003/`. Default serving port can be configured in `ignite-ui-cli.json` via `defaultProp` property. + +## Build + +Run `ig build` to build the application into an output directory. + +## Step by step mode + +If you want to get a guided experience through the available options, you can initialize the step by step mode that will help you to create and setup your new application, as well as update a project previously created with the Ignite UI CLI. To start the guide, simply run the `ig` command. + +## List templates + +The `ig list` command lists all available templates for this project. + +## Adding components + +Add a new component or template to the project passing component ID and choosing a name. + +`ig add ` + +The ID matches either a component ("grid", "category-chart", etc) or a predefined template. Predefined templates can provide either multiple components or fulfilling a specific use case like "form-validation", "master-detail" and so on. + +## Running unit tests + +Run `ig test` to execute the unit tests. + +## Commands Help + +`ig help` lists the available commands and provides a brief description of what they do. + +## Learn More + +To get more help on the IgniteUI CLI go check out the [IgniteUI CLI Wiki](https://github.com/IgniteUI/igniteui-cli/wiki). + +Learn more about Vite features in the [Vite documentation](https://vitejs.dev/guide/). + +To learn React, check out the [React documentation](https://reactjs.org/). + + + diff --git a/packages/cli/templates/react/igr-ts/projects/_base/files/__dot__eslintrc.cjs b/packages/cli/templates/react/igr-ts/projects/_base/files/__dot__eslintrc.cjs new file mode 100644 index 000000000..d6c953795 --- /dev/null +++ b/packages/cli/templates/react/igr-ts/projects/_base/files/__dot__eslintrc.cjs @@ -0,0 +1,18 @@ +module.exports = { + root: true, + env: { browser: true, es2020: true }, + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:react-hooks/recommended', + ], + ignorePatterns: ['dist', '.eslintrc.cjs'], + parser: '@typescript-eslint/parser', + plugins: ['react-refresh'], + rules: { + 'react-refresh/only-export-components': [ + 'warn', + { allowConstantExport: true }, + ], + }, +} diff --git a/packages/cli/templates/react/igr-ts/projects/_base/files/__dot__gitignore b/packages/cli/templates/react/igr-ts/projects/_base/files/__dot__gitignore new file mode 100644 index 000000000..4d29575de --- /dev/null +++ b/packages/cli/templates/react/igr-ts/projects/_base/files/__dot__gitignore @@ -0,0 +1,23 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# production +/build + +# misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local + +npm-debug.log* +yarn-debug.log* +yarn-error.log* diff --git a/packages/cli/templates/react/igr-ts/projects/_base/files/ignite-ui-cli.json b/packages/cli/templates/react/igr-ts/projects/_base/files/ignite-ui-cli.json new file mode 100644 index 000000000..5a3f94d1b --- /dev/null +++ b/packages/cli/templates/react/igr-ts/projects/_base/files/ignite-ui-cli.json @@ -0,0 +1,16 @@ +{ + "version": "$(cliVersion)", + "project": { + "defaultPort": 3003, + "framework": "react", + "projectType": "igr-ts", + "projectTemplate": "$(projectTemplate)", + "theme": "$(theme)", + "isBundle": false, + "components": [], + "sourceFiles": [], + "isShowcase": false, + "version": "" + }, + "build": {} +} diff --git a/packages/cli/templates/react/igr-ts/projects/_base/files/index.html b/packages/cli/templates/react/igr-ts/projects/_base/files/index.html new file mode 100644 index 000000000..63238fb05 --- /dev/null +++ b/packages/cli/templates/react/igr-ts/projects/_base/files/index.html @@ -0,0 +1,28 @@ + + + + + + + + IgniteUI for React + + + +
+ + + + diff --git a/packages/cli/templates/react/igr-ts/projects/_base/files/package.json b/packages/cli/templates/react/igr-ts/projects/_base/files/package.json new file mode 100644 index 000000000..89383b873 --- /dev/null +++ b/packages/cli/templates/react/igr-ts/projects/_base/files/package.json @@ -0,0 +1,51 @@ +{ + "name": "$(dash-name)", + "version": "0.1.0", + "private": true, + "type": "module", + "dependencies": { + "@testing-library/jest-dom": "^6.1.3", + "@testing-library/react": "^14.0.0", + "functions-have-names": "^1.2.3", + "igniteui-react": "18.3.0", + "igniteui-react-core": "18.3.0", + "react": "^18.2.0", + "react-app-polyfill": "^0.2.0", + "react-dom": "^18.2.0", + "react-router-dom": "^6.16.0", + "resize-observer-polyfill": "^1.5.1", + "vitest": "^0.34.4", + "igniteui-dockmanager": "^1.13.0" + }, + "devDependencies": { + "@types/react": "^18.2.15", + "@types/react-dom": "^18.2.7", + "@vitejs/plugin-react": "^4.0.3", + "@typescript-eslint/eslint-plugin": "^6.2.1", + "@typescript-eslint/parser": "^6.2.1", + "eslint": "^8.46.0", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-refresh": "^0.4.3", + "jsdom": "^22.1.0", + "typescript": "^5.0.2", + "vite": "^4.4.9", + "vitest-canvas-mock": "^0.3.3", + "igniteui-cli": "~$(cliVersion)" + }, + "scripts": { + "start": "vite", + "build": "tsc && vite build", + "preview": "vite preview", + "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", + "test": "vitest" + }, + "eslintConfig": { + "extends": "react-app" + }, + "browserslist": [ + ">0.2%", + "not dead", + "not ie <= 11", + "not op_mini all" + ] +} diff --git a/packages/cli/templates/react/igr-ts/projects/_base/files/public/favicon.ico b/packages/cli/templates/react/igr-ts/projects/_base/files/public/favicon.ico new file mode 100644 index 000000000..a11777cc4 Binary files /dev/null and b/packages/cli/templates/react/igr-ts/projects/_base/files/public/favicon.ico differ diff --git a/packages/cli/templates/react/igr-ts/projects/_base/files/src/app/App.test.tsx b/packages/cli/templates/react/igr-ts/projects/_base/files/src/app/App.test.tsx new file mode 100644 index 000000000..e15e55062 --- /dev/null +++ b/packages/cli/templates/react/igr-ts/projects/_base/files/src/app/App.test.tsx @@ -0,0 +1,8 @@ +import { expect, test } from 'vitest'; +import { render } from '@testing-library/react'; +import App from './App'; + +test('renders without crashing', () => { + const wrapper = render(); + expect(wrapper).toBeTruthy(); +}); diff --git a/packages/cli/templates/react/igr-ts/projects/_base/files/src/app/App.tsx b/packages/cli/templates/react/igr-ts/projects/_base/files/src/app/App.tsx new file mode 100644 index 000000000..4514300d8 --- /dev/null +++ b/packages/cli/templates/react/igr-ts/projects/_base/files/src/app/App.tsx @@ -0,0 +1,12 @@ +import { Outlet } from "react-router-dom"; + +export default function App() { + + return ( +
+
+ +
+
+ ) +} diff --git a/packages/cli/templates/react/igr-ts/projects/_base/files/src/app/app-routes.tsx b/packages/cli/templates/react/igr-ts/projects/_base/files/src/app/app-routes.tsx new file mode 100644 index 000000000..6abcae505 --- /dev/null +++ b/packages/cli/templates/react/igr-ts/projects/_base/files/src/app/app-routes.tsx @@ -0,0 +1,3 @@ +export const routes = [ + +]; diff --git a/packages/cli/templates/react/igr-ts/projects/_base/files/src/index.css b/packages/cli/templates/react/igr-ts/projects/_base/files/src/index.css new file mode 100644 index 000000000..cee5f348f --- /dev/null +++ b/packages/cli/templates/react/igr-ts/projects/_base/files/src/index.css @@ -0,0 +1,14 @@ +body { + margin: 0; + padding: 0; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", + "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", + sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +code { + font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", + monospace; +} diff --git a/packages/cli/templates/react/igr-ts/projects/_base/files/src/main.tsx b/packages/cli/templates/react/igr-ts/projects/_base/files/src/main.tsx new file mode 100644 index 000000000..cd7699009 --- /dev/null +++ b/packages/cli/templates/react/igr-ts/projects/_base/files/src/main.tsx @@ -0,0 +1,25 @@ +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import { createBrowserRouter, RouterProvider } from "react-router-dom"; +import { routes } from "./app/app-routes"; +import App from './app/App'; +import 'react-app-polyfill/ie11'; +import './index.css'; + +/** Required in IE11 for Charts */ +Number.isNaN = Number.isNaN || function(value) { + return value !== value; +} + +const router = createBrowserRouter([ + { + element: , + children: [...routes] + } +]); + +ReactDOM.createRoot(document.getElementById('root')).render( + + + +) diff --git a/packages/cli/templates/react/igr-ts/projects/_base/files/src/setupTests.ts b/packages/cli/templates/react/igr-ts/projects/_base/files/src/setupTests.ts new file mode 100644 index 000000000..ecb3d50cf --- /dev/null +++ b/packages/cli/templates/react/igr-ts/projects/_base/files/src/setupTests.ts @@ -0,0 +1,5 @@ +import '@testing-library/jest-dom' +import 'vitest-canvas-mock' +import ResizeObserver from 'resize-observer-polyfill' + +global.ResizeObserver = ResizeObserver; diff --git a/packages/cli/templates/react/igr-ts/projects/_base/files/tsconfig.json b/packages/cli/templates/react/igr-ts/projects/_base/files/tsconfig.json new file mode 100644 index 000000000..a39d6f990 --- /dev/null +++ b/packages/cli/templates/react/igr-ts/projects/_base/files/tsconfig.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + "composite": true, + "allowSyntheticDefaultImports": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": false, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src", "vite.config.ts"] +} diff --git a/packages/cli/templates/react/igr-ts/projects/_base/files/vite.config.ts b/packages/cli/templates/react/igr-ts/projects/_base/files/vite.config.ts new file mode 100644 index 000000000..4443712a3 --- /dev/null +++ b/packages/cli/templates/react/igr-ts/projects/_base/files/vite.config.ts @@ -0,0 +1,23 @@ +/// +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' + +// https://vitejs.dev/config/ +export default defineConfig({ + define: process.env.VITEST ? {} : { global: 'window' }, + plugins: [react()], + test: { + globals: true, + environment: 'jsdom', + setupFiles: ['./src/setupTests.ts'], + deps: { + inline: ['vitest-canvas-mock'], + }, + }, + resolve: { + mainFields: ['module'], + }, + server: { + port: 3003 + } +}) diff --git a/packages/cli/templates/react/igr-ts/projects/_base/index.ts b/packages/cli/templates/react/igr-ts/projects/_base/index.ts new file mode 100644 index 000000000..62d9a7cac --- /dev/null +++ b/packages/cli/templates/react/igr-ts/projects/_base/index.ts @@ -0,0 +1,47 @@ +import { ControlExtraConfiguration, defaultDelimiters, ProjectTemplate, Util } from "@igniteui/cli-core"; +import * as path from "path"; + +export class BaseIgrTsProject implements ProjectTemplate { + public id: string = "base"; + public name = "base"; + public description = "Ignite UI CLI project for React"; + public framework: string = "react"; + public projectType: string = "tsx"; + public dependencies: string[]; + public hasExtraConfiguration: boolean = false; + public delimiters = defaultDelimiters; + + public get templatePaths(): string[] { + return [path.join(__dirname, "files")]; + } + + public generateConfig(name: string, theme: string, ...options: any[]): {[key: string]: any} { + return this.getVariablesConfig(name, theme); + } + + public installModules(): void { + throw new Error("Method not implemented."); + } + public async upgradeIgniteUIPackages(projectPath: string, packagePath: string): Promise { + throw new Error("Method not implemented."); + } + public getExtraConfiguration(): ControlExtraConfiguration[] { + throw new Error("Method not implemented."); + } + public setExtraConfiguration(extraConfigKeys: {}) { + throw new Error("Method not implemented."); + } + + protected getVariablesConfig(name: string, theme: string) { + return { + name, + theme, + "cliVersion": Util.version(), + "dash-name": Util.lowerDashed(name), + "description": this.description, + "dot": ".", + "path": name, + "projectTemplate": this.id + }; + } +} diff --git a/packages/cli/templates/react/igr-ts/projects/_base_with_home/files/public/logo.svg b/packages/cli/templates/react/igr-ts/projects/_base_with_home/files/public/logo.svg new file mode 100644 index 000000000..6b60c1042 --- /dev/null +++ b/packages/cli/templates/react/igr-ts/projects/_base_with_home/files/public/logo.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/packages/cli/templates/react/igr-ts/projects/_base_with_home/files/src/app/app-routes.tsx b/packages/cli/templates/react/igr-ts/projects/_base_with_home/files/src/app/app-routes.tsx new file mode 100644 index 000000000..e34cacdf1 --- /dev/null +++ b/packages/cli/templates/react/igr-ts/projects/_base_with_home/files/src/app/app-routes.tsx @@ -0,0 +1,5 @@ +import Home from './home/home'; + +export const routes = [ + { path: '/', element: , text: 'Home' } +]; diff --git a/packages/cli/templates/react/igr-ts/projects/_base_with_home/files/src/app/home/home.tsx b/packages/cli/templates/react/igr-ts/projects/_base_with_home/files/src/app/home/home.tsx new file mode 100644 index 000000000..11e371b21 --- /dev/null +++ b/packages/cli/templates/react/igr-ts/projects/_base_with_home/files/src/app/home/home.tsx @@ -0,0 +1,24 @@ +import logo from "/logo.svg"; +import styles from "./style.module.css"; + +export default function App() { + const name = '$(name)'; + + return ( +
+
{name}
+
+ logo +

Welcome to Ignite UI for React!

+ + Learn More + +
+
+ ); +} diff --git a/packages/cli/templates/react/igr-ts/projects/_base_with_home/files/src/app/home/style.module.css b/packages/cli/templates/react/igr-ts/projects/_base_with_home/files/src/app/home/style.module.css new file mode 100644 index 000000000..c38cc0c19 --- /dev/null +++ b/packages/cli/templates/react/igr-ts/projects/_base_with_home/files/src/app/home/style.module.css @@ -0,0 +1,36 @@ +:local(.logo) { + animation: App-logo-spin infinite 20s linear; + height: 40vmin; +} + +:local(.header) { + padding-top: 5em; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + font-size: calc(10px + 2vmin); + color: #333; +} + +:local(.link) { + color: #61dafb; +} + +:local(.app__name) { + padding-top: 1em; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + font-size: calc(10px + 2vmin); +} + +@keyframes App-logo-spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} diff --git a/packages/cli/templates/react/igr-ts/projects/_base_with_home/index.ts b/packages/cli/templates/react/igr-ts/projects/_base_with_home/index.ts new file mode 100644 index 000000000..d875cff67 --- /dev/null +++ b/packages/cli/templates/react/igr-ts/projects/_base_with_home/index.ts @@ -0,0 +1,18 @@ +import { ProjectTemplate } from "@igniteui/cli-core"; +import * as path from "path"; +import { BaseIgrTsProject } from "../_base"; + +export class BaseWithHomeIgrTsProject extends BaseIgrTsProject implements ProjectTemplate { + public id: string = "base-with-home"; + public name = "Base with home"; + public description = "React project structure with home view"; + public dependencies: string[] = []; + public framework: string = "react"; + public projectType: string = "igr-ts"; + public hasExtraConfiguration: boolean = false; + + public get templatePaths(): string[] { + return [...super.templatePaths, path.join(__dirname, "files")]; + } +} +export default new BaseWithHomeIgrTsProject(); diff --git a/packages/cli/templates/react/igr-ts/projects/base/index.ts b/packages/cli/templates/react/igr-ts/projects/base/index.ts new file mode 100644 index 000000000..269deb770 --- /dev/null +++ b/packages/cli/templates/react/igr-ts/projects/base/index.ts @@ -0,0 +1,18 @@ +import { ProjectTemplate } from "@igniteui/cli-core"; +import * as path from "path"; +import { BaseIgrTsProject } from "../_base"; + +export class BasePageIgrTsProject extends BaseIgrTsProject implements ProjectTemplate { + public id: string = "base"; + public name = "Base project"; + public description = "Empty project structure with routing"; + public dependencies: string[] = []; + public framework: string = "react"; + public projectType: string = "igr-ts"; + public hasExtraConfiguration: boolean = false; + + public get templatePaths(): string[] { + return [...super.templatePaths, path.join(__dirname, "files")]; + } +} +export default new BasePageIgrTsProject(); diff --git a/packages/cli/templates/react/igr-ts/projects/empty/index.ts b/packages/cli/templates/react/igr-ts/projects/empty/index.ts new file mode 100644 index 000000000..86b8428ae --- /dev/null +++ b/packages/cli/templates/react/igr-ts/projects/empty/index.ts @@ -0,0 +1,18 @@ +import { ProjectTemplate } from "@igniteui/cli-core"; +import * as path from "path"; +import { BaseWithHomeIgrTsProject } from "../_base_with_home"; + +export class EmptyIgrTsProject extends BaseWithHomeIgrTsProject implements ProjectTemplate { + public id: string = "empty"; + public name = "Empty project"; + public description = "Empty project structure with home page and routing"; + public dependencies: string[] = []; + public framework: string = "react"; + public projectType: string = "igr-ts"; + public hasExtraConfiguration: boolean = false; + + public get templatePaths(): string[] { + return [...super.templatePaths, path.join(__dirname, "files")]; + } +} +export default new EmptyIgrTsProject(); diff --git a/packages/cli/templates/react/igr-ts/projects/top-nav/files/src/app/App.css b/packages/cli/templates/react/igr-ts/projects/top-nav/files/src/app/App.css new file mode 100644 index 000000000..41a9dc40f --- /dev/null +++ b/packages/cli/templates/react/igr-ts/projects/top-nav/files/src/app/App.css @@ -0,0 +1,62 @@ +.app { + text-align: center; + display: flex; + flex-flow: column; + min-height: 100vh; +} + +.content { + flex: 1 0 auto; + padding: 1em; +} + +/* quick nav menubar */ +nav, +.app__name { + background-color: rgb(0, 153, 255); + box-sizing: border-box; +} +.app__name { + font-weight: 600; + font-size: 24px; + line-height: 32px; + padding-left: 24px; + text-align: left; + color: #fff; + padding-bottom: 8px; +} + +nav ul { + list-style: none; + display: flex; + justify-content: flex-start; + flex-wrap: wrap; + margin: 0; + padding: 0; +} + +nav ul a { + color: #fff; + line-height: 2; + text-decoration: none; + margin: 0 5px; +} + +nav ul a.active { + color: #fff; + font-weight: 600; +} + +nav ul li { + margin: 0px 16px; + box-sizing: border-box; + border-bottom: 1px solid transparent; +} + +nav ul li.active { + border-bottom: 2px solid #fff; +} + +nav ul li:not(.active):hover { + border-bottom: 1px solid #fff; +} diff --git a/packages/cli/templates/react/igr-ts/projects/top-nav/files/src/app/App.tsx b/packages/cli/templates/react/igr-ts/projects/top-nav/files/src/app/App.tsx new file mode 100644 index 000000000..7e1d3a0a9 --- /dev/null +++ b/packages/cli/templates/react/igr-ts/projects/top-nav/files/src/app/App.tsx @@ -0,0 +1,18 @@ +import { Outlet } from "react-router-dom"; +import { routes } from './app-routes'; +import NavigationHeader from "../components/navigation-header"; +import "./App.css"; + +export default function App() { + const name = "$(name)"; + + return ( +
+
{name}
+ +
+ +
+
+ ); +} diff --git a/packages/cli/templates/react/igr-ts/projects/top-nav/files/src/components/navigation-header/index.tsx b/packages/cli/templates/react/igr-ts/projects/top-nav/files/src/components/navigation-header/index.tsx new file mode 100644 index 000000000..fc35bf948 --- /dev/null +++ b/packages/cli/templates/react/igr-ts/projects/top-nav/files/src/components/navigation-header/index.tsx @@ -0,0 +1,29 @@ +import { useState, useEffect } from 'react'; +import { Link } from 'react-router-dom'; + +export default function NavigationHeader({ routes }) { + const [state, setState] = useState({ activeItem: null }); + + function handleClick(index) { + setState({ activeItem: index }); + } + + useEffect(() => { + let currentRoute = window.location.href.split(window.location.origin)[1]; + if (!currentRoute) { + currentRoute = '/' + } + const activeItem = routes.findIndex((route) => route.path === currentRoute); + setState({ activeItem }); + }, [routes]); + + return ( + + ) +} diff --git a/packages/cli/templates/react/igr-ts/projects/top-nav/index.ts b/packages/cli/templates/react/igr-ts/projects/top-nav/index.ts new file mode 100644 index 000000000..48d4cab80 --- /dev/null +++ b/packages/cli/templates/react/igr-ts/projects/top-nav/index.ts @@ -0,0 +1,18 @@ +import { ProjectTemplate } from "@igniteui/cli-core"; +import * as path from "path"; +import { BaseWithHomeIgrTsProject } from "../_base_with_home"; + +export class TopNavIgrTsProject extends BaseWithHomeIgrTsProject implements ProjectTemplate { + public id: string = "top-nav"; + public name = "Default top navigation"; + public description = "Project structure with top navigation menu"; + public dependencies: string[] = []; + public framework: string = "react"; + public projectType: string = "igr-ts"; + public hasExtraConfiguration: boolean = false; + + public get templatePaths(): string[] { + return [...super.templatePaths, path.join(__dirname, "files")]; + } +} +export default new TopNavIgrTsProject(); diff --git a/packages/cli/templates/react/igr-ts/radial-gauge/default/files/src/app/__path__/__filePrefix__.test.tsx b/packages/cli/templates/react/igr-ts/radial-gauge/default/files/src/app/__path__/__filePrefix__.test.tsx new file mode 100644 index 000000000..d8d91aeb3 --- /dev/null +++ b/packages/cli/templates/react/igr-ts/radial-gauge/default/files/src/app/__path__/__filePrefix__.test.tsx @@ -0,0 +1,8 @@ +import { expect, test } from 'vitest'; +import { render } from '@testing-library/react'; +import $(ClassName) from './index'; + +test('renders $(ClassName) component', () => { + const wrapper = render(<$(ClassName) />); + expect(wrapper).toBeTruthy(); +}); diff --git a/packages/cli/templates/react/igr-ts/radial-gauge/default/files/src/app/__path__/__filePrefix__.tsx b/packages/cli/templates/react/igr-ts/radial-gauge/default/files/src/app/__path__/__filePrefix__.tsx new file mode 100644 index 000000000..1a8fe0f48 --- /dev/null +++ b/packages/cli/templates/react/igr-ts/radial-gauge/default/files/src/app/__path__/__filePrefix__.tsx @@ -0,0 +1,60 @@ +import { IgrRadialGaugeModule } from 'igniteui-react-gauges'; +import { IgrRadialGauge } from 'igniteui-react-gauges'; +import style from './style.module.css'; + +IgrRadialGaugeModule.register(); + + +export default function $(ClassName)() { + const title = 'Radial Gauge'; + + return ( +
+

{title}

+
+ Read more on the  + + official documentation page + +
+
+
+ + +
+
+ + +
+
+
+ ) +} diff --git a/packages/cli/templates/react/igr-ts/radial-gauge/default/files/src/app/__path__/style.module.css b/packages/cli/templates/react/igr-ts/radial-gauge/default/files/src/app/__path__/style.module.css new file mode 100644 index 000000000..215c1ac12 --- /dev/null +++ b/packages/cli/templates/react/igr-ts/radial-gauge/default/files/src/app/__path__/style.module.css @@ -0,0 +1,12 @@ +:local(.container) { + display: flex; + flex-flow: row; + justify-content: space-around; +} + +:local(.title) { + color: rgb(0, 153, 255); +} +:local(.gauge) { + width: 50%; +} diff --git a/packages/cli/templates/react/igr-ts/radial-gauge/default/index.ts b/packages/cli/templates/react/igr-ts/radial-gauge/default/index.ts new file mode 100644 index 000000000..fe123847a --- /dev/null +++ b/packages/cli/templates/react/igr-ts/radial-gauge/default/index.ts @@ -0,0 +1,18 @@ +import { IgniteUIForReactTemplate } from "../../../../../lib/templates/IgniteUIForReactTemplate"; + +class IgrTsRadialGaugeTemplate extends IgniteUIForReactTemplate { + constructor() { + super(__dirname); + this.components = ["Radial Gauge"]; + this.controlGroup = "Gauges"; + this.listInComponentTemplates = true; + this.id = "radial-gauge"; + this.projectType = "igr-ts"; + this.name = "Radial Gauge"; + this.description = `provides a number of visual elements, like a needle, tick marks, ranges + and labels, in order to create a predefined shape and scale.`; + // TODO: read version from igniteui-react-core in package.json + this.packages = ["igniteui-react-gauges@18.3.0"]; + } +} +module.exports = new IgrTsRadialGaugeTemplate(); diff --git a/packages/cli/templates/react/igr-ts/radial-gauge/index.ts b/packages/cli/templates/react/igr-ts/radial-gauge/index.ts new file mode 100644 index 000000000..d297e1573 --- /dev/null +++ b/packages/cli/templates/react/igr-ts/radial-gauge/index.ts @@ -0,0 +1,16 @@ + +import { BaseComponent } from "@igniteui/cli-core"; + +class IgrTsRadialGaugeComponent extends BaseComponent { + /** + * + */ + constructor() { + super(__dirname); + this.name = "Radial Gauge"; + this.group = "Gauges"; + this.description = `provides a number of visual elements, like a needle, tick marks, ranges + and labels, in order to create a predefined shape and scale.`; + } +} +module.exports = new IgrTsRadialGaugeComponent(); diff --git a/packages/cli/templates/react/index.ts b/packages/cli/templates/react/index.ts index dda6ab5b2..c33bf0c53 100644 --- a/packages/cli/templates/react/index.ts +++ b/packages/cli/templates/react/index.ts @@ -11,6 +11,7 @@ class ReactFramework implements Framework { this.projectLibraries = []; this.projectLibraries.push(require("./es6") as ProjectLibrary); this.projectLibraries.push(require("./igr-es6") as ProjectLibrary); + this.projectLibraries.push(require("./igr-ts") as ProjectLibrary); } } export = new ReactFramework() as Framework; diff --git a/packages/core/package.json b/packages/core/package.json index e8632c36d..c90485d44 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@igniteui/cli-core", - "version": "13.0.2", + "version": "12.1.0-beta.4", "description": "Base types and functionality for Ignite UI CLI", "repository": { "type": "git", diff --git a/packages/core/packages/PackageManager.ts b/packages/core/packages/PackageManager.ts index bab6378a2..c0c04c7ce 100644 --- a/packages/core/packages/PackageManager.ts +++ b/packages/core/packages/PackageManager.ts @@ -95,7 +95,7 @@ export class PackageManager { case "npm": /* passes through */ default: - command = `${managerCommand} install --quiet`; + command = `${managerCommand} install --force --quiet`; break; } await this.flushQueue(false); @@ -277,7 +277,7 @@ export class PackageManager { case "npm": /* passes through */ default: - return `${managerCommand} install ${packageName} --quiet --save`; + return `${managerCommand} install ${packageName} --force --quiet --save`; } } diff --git a/packages/core/util/Util.ts b/packages/core/util/Util.ts index f35903f5f..d73104de8 100644 --- a/packages/core/util/Util.ts +++ b/packages/core/util/Util.ts @@ -405,6 +405,8 @@ export class Util { specificPath = path.join("src", "components"); } else if (framework === "react" && projectType === "igr-es6") { specificPath = path.join("src", "views"); + } else if (framework === "react" && projectType === "igr-ts") { + specificPath = path.join("src", "app"); } else if (framework === "webcomponents" && projectType === "igc-ts") { specificPath = path.join("src", "app"); } diff --git a/packages/igx-templates/package.json b/packages/igx-templates/package.json index d1db2898b..cc96e691d 100644 --- a/packages/igx-templates/package.json +++ b/packages/igx-templates/package.json @@ -1,6 +1,6 @@ { "name": "@igniteui/angular-templates", - "version": "17.0.1302", + "version": "17.0.1210-beta.4", "description": "Templates for Ignite UI for Angular projects and components", "repository": { "type": "git", @@ -12,7 +12,7 @@ "author": "Infragistics", "license": "MIT", "dependencies": { - "@igniteui/cli-core": "~13.0.2", + "@igniteui/cli-core": "~12.1.0-beta.4", "typescript": "~4.7.2" } } diff --git a/packages/ng-schematics/package.json b/packages/ng-schematics/package.json index dc4f2ab08..7997443dd 100644 --- a/packages/ng-schematics/package.json +++ b/packages/ng-schematics/package.json @@ -1,6 +1,6 @@ { "name": "@igniteui/angular-schematics", - "version": "17.0.1302", + "version": "17.0.1210-beta.4", "description": "Ignite UI for Angular Schematics for ng new and ng generate", "repository": { "type": "git", @@ -20,8 +20,8 @@ "dependencies": { "@angular-devkit/core": "~14.0.0", "@angular-devkit/schematics": "~14.0.0", - "@igniteui/angular-templates": "~17.0.1302", - "@igniteui/cli-core": "~13.0.2", + "@igniteui/angular-templates": "~17.0.1210-beta.4", + "@igniteui/cli-core": "~12.1.0-beta.4", "@schematics/angular": "~14.0.0", "rxjs": "^6.6.3" }, diff --git a/spec/unit/packageManager-spec.ts b/spec/unit/packageManager-spec.ts index ab4491c30..24fa9a237 100644 --- a/spec/unit/packageManager-spec.ts +++ b/spec/unit/packageManager-spec.ts @@ -223,7 +223,7 @@ describe("Unit - Package Manager", () => { await TestPackageManager.ensureIgniteUISource(true, mockTemplateMgr, true); expect(TestPackageManager.addPackage).toHaveBeenCalledWith(`@infragistics/ignite-ui-full@"~20.1"`, true); expect(Util.execSync).toHaveBeenCalledWith( - `npm install @infragistics/ignite-ui-full@"~20.1" --quiet --save`, + `npm install @infragistics/ignite-ui-full@"~20.1" --force --quiet --save`, jasmine.any(Object) ); expect(TestPackageManager.removePackage).toHaveBeenCalledWith("ignite-ui", true); @@ -232,7 +232,7 @@ describe("Unit - Package Manager", () => { await TestPackageManager.ensureIgniteUISource(true, mockTemplateMgr, true); expect(TestPackageManager.addPackage).toHaveBeenCalledWith(`@infragistics/ignite-ui-full@"^17.1"`, true); expect(Util.execSync).toHaveBeenCalledWith( - `npm install @infragistics/ignite-ui-full@"^17.1" --quiet --save`, + `npm install @infragistics/ignite-ui-full@"^17.1" --force --quiet --save`, jasmine.any(Object) ); @@ -240,7 +240,7 @@ describe("Unit - Package Manager", () => { await TestPackageManager.ensureIgniteUISource(true, mockTemplateMgr, true); expect(TestPackageManager.addPackage).toHaveBeenCalledWith(`@infragistics/ignite-ui-full@">=0.1.0 <0.2.0"`, true); expect(Util.execSync).toHaveBeenCalledWith( - `npm install @infragistics/ignite-ui-full@">=0.1.0 <0.2.0" --quiet --save`, + `npm install @infragistics/ignite-ui-full@">=0.1.0 <0.2.0" --force --quiet --save`, jasmine.any(Object) ); done(); @@ -263,7 +263,7 @@ describe("Unit - Package Manager", () => { expect(Util.log).toHaveBeenCalledWith(`Installing npm packages`); expect(Util.log).toHaveBeenCalledWith(`Error installing npm packages.`); expect(Util.log).toHaveBeenCalledWith(`Example`); - expect(Util.execSync).toHaveBeenCalledWith(`npm install --quiet`, + expect(Util.execSync).toHaveBeenCalledWith(`npm install --force --quiet`, { stdio: ["inherit"], killSignal: "SIGINT" }); expect(ProjectConfig.setConfig).toHaveBeenCalledWith({ packagesInstalled: true }); done(); @@ -280,7 +280,7 @@ describe("Unit - Package Manager", () => { expect(Util.log).toHaveBeenCalledTimes(2); expect(Util.log).toHaveBeenCalledWith(`Installing npm packages`); expect(Util.log).toHaveBeenCalledWith(`Packages installed successfully`); - expect(Util.execSync).toHaveBeenCalledWith(`npm install --quiet`, + expect(Util.execSync).toHaveBeenCalledWith(`npm install --force --quiet`, { stdio: ["inherit"], killSignal: "SIGINT" }); expect(ProjectConfig.setConfig).toHaveBeenCalledWith({ packagesInstalled: true }); done(); @@ -301,7 +301,7 @@ describe("Unit - Package Manager", () => { await PackageManager.installPackages(true); expect(Util.log).toHaveBeenCalledTimes(1); expect(Util.log).toHaveBeenCalledWith(`Installing npm packages`); - expect(Util.execSync).toHaveBeenCalledWith(`npm install --quiet`, + expect(Util.execSync).toHaveBeenCalledWith(`npm install --force --quiet`, { stdio: ["inherit"], killSignal: "SIGINT" }); expect(process.exit).toHaveBeenCalled(); expect(ProjectConfig.setConfig).toHaveBeenCalledTimes(0); @@ -345,7 +345,7 @@ describe("Unit - Package Manager", () => { expect(Util.log).toHaveBeenCalledWith(`Error installing package example-package with npm`); expect(Util.log).toHaveBeenCalledWith(`Error`); expect(Util.execSync).toHaveBeenCalledWith( - `npm install example-package --quiet --save`, { stdio: "pipe", encoding: "utf8" }); + `npm install example-package --force --quiet --save`, { stdio: "pipe", encoding: "utf8" }); done(); }); it("Should run addPackage properly without error code", async done => { @@ -355,7 +355,7 @@ describe("Unit - Package Manager", () => { expect(Util.log).toHaveBeenCalledTimes(1); expect(Util.log).toHaveBeenCalledWith(`Package example-package installed successfully`); expect(Util.execSync).toHaveBeenCalledWith( - `npm install example-package --quiet --save`, { stdio: "pipe", encoding: "utf8" }); + `npm install example-package --force --quiet --save`, { stdio: "pipe", encoding: "utf8" }); done(); }); @@ -380,7 +380,7 @@ describe("Unit - Package Manager", () => { expect(Util.log).toHaveBeenCalledTimes(0); expect(cp.exec).toHaveBeenCalledTimes(1); expect(cp.exec).toHaveBeenCalledWith( - `npm install test-pack --quiet --no-save`, {}, jasmine.any(Function)); + `npm install test-pack --force --quiet --no-save`, {}, jasmine.any(Function)); done(); });