# This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. import os import re from mach.decorators import Command, CommandArgument LICENSE_HEADER = """/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ """ JS_HEADER = """{license} import {{ html }} from "../vendor/lit.all.mjs"; import {{ MozLitElement }} from "../lit-utils.mjs"; /** * Component description goes here. * * @tagname {element_name} * @property {{string}} variant - Property description goes here */ export default class {class_name} extends MozLitElement {{ static properties = {{ variant: {{ type: String }}, }}; // Use a relative URL in storybook to get faster reloads on style changes. static stylesheetUrl = window.IS_STORYBOOK ? "./{element_name}/{element_name}.css" : "chrome://global/content/elements/{element_name}.css"; constructor() {{ super(); this.variant = "default"; }} render() {{ return html`
Variant type: ${{this.variant}}
`; }} }} customElements.define("{element_name}", {class_name}); """ STORY_HEADER = """{license} import {{ html }} from "../vendor/lit.all.mjs"; // eslint-disable-next-line import/no-unassigned-import import "./{element_name}.mjs"; export default {{ title: "UI Widgets/{story_name}", component: "{element_name}", argTypes: {{ variant: {{ options: ["default", "other"], control: {{ type: "select" }}, }}, }}, }}; const Template = ({{ variant }}) => html` <{element_name} .variant=${{variant}}> `; export const Default = Template.bind({{}}); Default.args = {{ variant: "default", }}; """ def run_mach(command_context, cmd, **kwargs): return command_context._mach_context.commands.dispatch( cmd, command_context._mach_context, **kwargs ) def run_npm(command_context, args): return run_mach( command_context, "npm", args=[*args, "--prefix=browser/components/storybook"] ) @Command( "addwidget", category="misc", description="Scaffold a front-end component.", ) @CommandArgument( "names", nargs="+", help="Component names to create in kebab-case, eg. my-card.", ) def addwidget(command_context, names): for name in names: component_dir = "toolkit/content/widgets/{0}".format(name) try: os.mkdir(component_dir) except FileExistsError: pass with open("{0}/{1}.mjs".format(component_dir, name), "w", newline="\n") as f: class_name = "".join(p.capitalize() for p in name.split("-")) f.write( JS_HEADER.format( license=LICENSE_HEADER, element_name=name, class_name=class_name, ) ) with open("{0}/{1}.css".format(component_dir, name), "w", newline="\n") as f: f.write(LICENSE_HEADER) test_name = name.replace("-", "_") test_path = "toolkit/content/tests/widgets/test_{0}.html".format(test_name) jar_path = "toolkit/content/jar.mn" jar_lines = None with open(jar_path, "r") as f: jar_lines = f.readlines() elements_startswith = " content/global/elements/" new_css_line = "{0}{1}.css (widgets/{1}/{1}.css)\n".format( elements_startswith, name ) new_js_line = "{0}{1}.mjs (widgets/{1}/{1}.mjs)\n".format( elements_startswith, name ) new_jar_lines = [] found_elements_section = False added_widget = False for line in jar_lines: if line.startswith(elements_startswith): found_elements_section = True if found_elements_section and not added_widget and line > new_css_line: added_widget = True new_jar_lines.append(new_css_line) new_jar_lines.append(new_js_line) new_jar_lines.append(line) with open(jar_path, "w", newline="\n") as f: f.write("".join(new_jar_lines)) story_path = "{0}/{1}.stories.mjs".format(component_dir, name) with open(story_path, "w", newline="\n") as f: story_name = " ".join( name for name in re.findall(r"[A-Z][a-z]+", class_name) if name != "Moz" ) f.write( STORY_HEADER.format( license=LICENSE_HEADER, element_name=name, story_name=story_name, ) ) run_mach( command_context, "addtest", argv=[test_path, "--suite", "mochitest-chrome"] )