Skip to main content

create-new-component

Create a new Lit web component following project conventions, including specification, component class, styles, tests, Storybook story, and proper exports

Zur Installation springen

Quellinformationen

Repository
IgniteUI/igniteui-webcomponents
Letzte Quellaktivität
21. September 2026 um 19:38
Erkannte Sprache von SKILL.md
Englisch
Sterne
170
Forks
11

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
name
create-new-component
description
Create a new Lit web component following project conventions, including specification, component class, styles, tests, Storybook story, and proper exports
# Create New Component Creates a new Lit web component that follows the project conventions. Read the [Coding Guidelines](../../CODING_GUIDELINES.md) for the rules behind the steps below. ## When to Use - "Create a new progress-bar component" - "Add a new stepper component to the library" ## Related Skills - [add-component-property](../add-component-property/) - Add properties after creating the component - [update-component-styles](../update-component-styles/) - Modify component styles ## Required Context Confirm with the user before starting: - **Name**: `progress-bar` → tag `igc-progress-bar`, class `IgcProgressBarComponent` - **Purpose**: one-line description used verbatim in the public API docs - **Public API**: initial properties, events, slots, CSS parts - **Kind**: plain display component, container, or form-associated control ## Steps ### 1. Create the directory structure ```bash mkdir -p src/components/[name]/themes/{light,dark,shared} ``` ### 2. Write the specification `src/components/[name]/spec.md` is the behavioral contract of the component, and it comes before the implementation — writing it is how the public API, the keyboard model and the ARIA semantics get decided. Copy the structure from `src/components/splitter/spec.md`, the reference for every spec in the repository; the rules below summarize [Specifications](../../CODING_GUIDELINES.md#specifications): ```markdown # [Name] specification - [[Name] specification](#name-specification) - [Revision history](#revision-history) ... ## Revision history | Version | Date | Notes | | ------: | ---------- | --------------------- | | 1 | YYYY-MM-DD | Initial specification | ## Overview ### Key features ### Acceptance criteria ## User stories ### End-user stories ### Developer stories ## Functionality ### End-user experience ### Developer experience ### Localization ### Keyboard interactions ## API ### Properties and attributes ### Methods ### Events ### Slots ### CSS Shadow parts ## Test scenarios ## Assumptions and limitations ## Accessibility ### ARIA roles and properties ### Keyboard support ### Right to Left support ``` Rules: - **One `spec.md` per directory.** A directory holding several components documents all of them in one file, with a table or a subsection per component — the button and the icon button share `button/spec.md`, the two progress indicators share `progress/spec.md`. - **The table of contents is hand-maintained.** Every `##` and `###` heading gets an entry, and the anchors follow the GitHub slug rules (lowercase, punctuation dropped, spaces to hyphens). - **No ownership, approval or sign-off sections**, and no author column in the revision history. A new component starts at version 1. - **Preserve design hand-off links.** A Figma link belongs under `### End-user experience`. Say so plainly when there is none rather than leaving a placeholder. - **Tag names are fine here.** The prose rule against `igc-` names applies to JSDoc, which ships into the framework wrappers; a spec is repository documentation and names the elements directly. - **Link sibling specs relatively** — `[the popover](../popover/spec.md)`, and to a heading with `../popover/spec.md#keyboard-interactions`. - **Internal components get a spec too** when they carry behavior others depend on. The validation container, the popover and the focus trap each have one. Fill in `## Test scenarios` once the tests exist; see step 7. ### 3. Create the component class `src/components/[name]/[name].ts`: ```ts import { html, LitElement } from 'lit'; import { property } from 'lit/decorators.js'; import { registerComponent } from '#internals/definitions/register.js'; import { addThemingController } from '#theming/theming-controller.js'; import { styles } from './themes/[name].base.css.js'; import { styles as shared } from './themes/shared/[name].common.css.js'; import { all } from './themes/themes.js'; /** * [One-line description of what the component is for.] * * @element igc-[name] * * @slot - [Default slot description] * * @csspart base - [Description of the CSS part] */ export default class Igc[Name]Component extends LitElement { public static readonly tagName = 'igc-[name]'; public static override styles = [styles, shared]; /* blazorSuppress */ public static register(): void { registerComponent(Igc[Name]Component); } //#region Public attributes and properties /** * [Property description] * @attr some-prop * @default 'default-value' */ @property({ reflect: true }) public someProp = 'default-value'; //#endregion constructor() { super(); addThemingController(this, all); } protected override render() { return html` <div part="base"> <slot></slot> </div> `; } } declare global { interface HTMLElementTagNameMap { 'igc-[name]': Igc[Name]Component; } } ``` Key points: - Cross-cutting imports go through `#internals/*`, `#theming/*` and `#animations/*`; component imports stay relative. All specifiers end in `.js`. - `registerComponent(Self, ...dependencies)` — pass every component rendered in the template. - Region fences and the member order follow the [component structure](../../CODING_GUIDELINES.md#components). - Internal API is `_`-prefixed; no native private fields (`#`). - Only primitives may be attributes; complex types get `attribute: false`. - For ARIA use `addInternalsController`; for keyboard use `addKeybindings`; for slot state use `addSlotController`. See the [controllers table](../../CODING_GUIDELINES.md#controllers). ### 4. Create the SCSS files SCSS resolves against the `src` and `node_modules` load paths — use package-style specifiers, never relative ones. Indentation in SCSS is 4 spaces. `themes/[name].base.scss` — structure and layout, theme-agnostic: ```scss @use 'styles/common/component'; @use 'styles/utilities' as *; :host { display: block; } [part~='base'] { // Structural styles } ``` `themes/light/_themes.scss` — digest the schemas from `igniteui-theming`: ```scss @use 'styles/utilities' as *; @use 'igniteui-theming/sass/themes/schemas/components/light/[name]' as *; $base: digest-schema($light-[name]); $material: digest-schema($material-[name]); $bootstrap: digest-schema($bootstrap-[name]); $fluent: digest-schema($fluent-[name]); $indigo: digest-schema($indigo-[name]); ``` `themes/dark/_themes.scss` mirrors it with the dark schemas (no `$base`). Then, per theme: - `themes/light/[name].shared.scss` — emits the full variable set from `$base` - `themes/light/[name].{bootstrap,material,fluent,indigo}.scss` — `diff($base, $theme)` - `themes/dark/[name].{bootstrap,material,fluent,indigo}.scss` — `diff(light.$base, $theme)` - `themes/shared/[name].common.scss` — cross-theme styling that reads the variables - `themes/shared/[name].{bootstrap,material,fluent,indigo}.scss` — per-theme structural tweaks (optional) ```scss // themes/light/[name].bootstrap.scss @use 'styles/utilities' as *; @use 'themes' as *; $theme: $bootstrap; :host { @include css-vars-from-theme(diff($base, $theme)); } ``` > [!NOTE] > A brand-new component only has schemas once they are added to `igniteui-theming`. Until > then, declare the CSS variables directly in `themes/shared/[name].common.scss` and keep the > light/dark files empty rather than inventing values per theme. ### 5. Create the theme aggregator `themes/themes.ts` is the only hand-written TypeScript file in the directory: ```ts import { css } from 'lit'; import type { Themes } from '#theming/types.js'; // Dark Overrides import { styles as bootstrapDark } from './dark/[name].bootstrap.css.js'; import { styles as fluentDark } from './dark/[name].fluent.css.js'; import { styles as indigoDark } from './dark/[name].indigo.css.js'; import { styles as materialDark } from './dark/[name].material.css.js'; // Light Overrides import { styles as bootstrapLight } from './light/[name].bootstrap.css.js'; import { styles as fluentLight } from './light/[name].fluent.css.js'; import { styles as indigoLight } from './light/[name].indigo.css.js'; import { styles as materialLight } from './light/[name].material.css.js'; import { styles as shared } from './light/[name].shared.css.js'; const light = { shared: css` ${shared} `, bootstrap: css` ${bootstrapLight} `, material: css` ${materialLight} `, fluent: css` ${fluentLight} `, indigo: css` ${indigoLight} `, }; const dark = { shared: css` ${shared} `, bootstrap: css` ${bootstrapDark} `, material: css` ${materialDark} `, fluent: css` ${fluentDark} `, indigo: css` ${indigoDark} `, }; export const all: Themes = { light, dark }; ``` ### 6. Transpile the styles ```bash npm run build:styles ``` This generates a `.css.ts` next to each `.scss` (imported as `.css.js`). The generated files are **gitignored** — never edit or commit them. Only files matching `*.{base,common,shared,material,bootstrap,indigo,fluent}.scss` are picked up; anything else is silently skipped. ### 7. Write the tests `src/components/[name]/[name].spec.ts`: ```ts import { elementUpdated, expect, fixture, html } from '@open-wc/testing'; import { defineComponents } from '#internals/definitions/defineComponents.js'; import Igc[Name]Component from './[name].js'; describe('[Name]', () => { before(() => { defineComponents(Igc[Name]Component); }); it('passes the a11y audit', async () => { const el = await fixture<Igc[Name]Component>(html`<igc-[name]></igc-[name]>`); await expect(el).shadowDom.to.be.accessible(); await expect(el).to.be.accessible(); }); it('is initialized with the proper default values', async () => { const el = await fixture<Igc[Name]Component>(html`<igc-[name]></igc-[name]>`); expect(el.someProp).to.equal('default-value'); }); it('updates on property change', async () => { const el = await fixture<Igc[Name]Component>(html`<igc-[name]></igc-[name]>`); el.someProp = 'new-value'; await elementUpdated(el); expect(el.someProp).to.equal('new-value'); }); }); ``` Drive user interaction through the shared simulators (`simulateClick`, `simulateKeyboard`, …) from `#internals/testing/simulate.spec.js`, and use `createFormAssociatedTestBed` from `#internals/testing/form-testbed.spec.js` for form-associated controls. Now fill in the `## Test scenarios` section of the spec. It mirrors the suite that actually exists: one subsection per `describe` block, keeping its name, with the scenarios numbered contiguously across the whole section. Name the shared runners the suite uses (`runValidationContainerTests`, `runAriaProjectionTests`, `runInvokerCommandsTests`, …) rather than restating what they assert, and list the suite files in a table when there is more than one: ```markdown | Suite | File |
Auf GitHub ansehen
Diese SKILL.md ist sehr gross, daher zeigt SkillsMP hier nur den ersten Abschnitt. Auf GitHub ansehen