Skip to content

Component design conventions

Jamie Maguire edited this page Oct 18, 2024 · 3 revisions

PIE Component Authoring Conventions

This is a small style guide for writing styles, Typescript, and tests for building PIE web components.

Styling

  • We use full class names in modifiers but also nest them. Example:

    .foo {
      &.foo--bar {
        color: saddlebrown;
      }
    }

Typescript

  • All public methods should have JSDoc comments.

  • All properties in defs.ts should have comments.

  • Private properties should start with an underscore, such as private _locale.

  • Default property values should be declared in defs.ts.

  • Prop types should either be inferred from defaultProps or specified using the component’s prop interface:

    • Good: public size = defaultProps.size;

    • Good: public name: TextInputProps['name'];

    • Bad: public type: TextInputProps['type'] = defaultProps.type;

    • Bad: public readonly = false;

  • It is unnecessary to add ? to types in index.ts, this should be applied in defs.ts instead.

  • If we know for sure that a @query element will exist (i.e., the component is completely useless without it, like the textarea element in pie-textarea), it is acceptable to use ! to tell TypeScript that it will exist.

  • Required properties can also use ! as long as they also use the requiredProperty decorator.

  • All props are destructured into the render method (up for discussion).

  • Type imports should use the type keyword.

  • Some lifecycle methods do not need super calls (consult Lit docs and consider a linting rule).

    • Consider a lint rule to enforce super calls being the first thing when needed.
  • Properties of a string union type (e.g., size, status, variant) should use the validPropertyValues decorator.

  • Ordering of code in index.ts

    1. Properties first.

    2. Lifecycle methods.

    3. Other methods

    4. Render methods last (except for styles export).

  • Use classMap for adding conditional (and static) classes to elements.

  • Keep logic out of the main render method where possible/practical.

  • Consider splitting the render method into multiple smaller ones, e.g., renderLabel, renderActions, etc.

  • All properties should have a type declaration, e.g.:

    @property({ type: String })
  • Component properties should use camelCase naming. For example: hasBackButton.

    • The only exception to this is for properties that are tied to native HTML attributes. For these, we copy the same casing as the native one. For example: formenctype and formnovalidate.
  • If the property is a boolean value, we aim to prepend the name with is. For example: isOpen, isStrong.

    • The only exception to this boolean naming rule is when the property shares a name with a native HTML attribute, such as disabled or checked.
  • Try to avoid type assertions in TypeScript, as it often implies a larger issue in your code:

    const foo = bar as string; // avoid this
  • Where a return type is obvious and inferred, there is no need to specify it.

Testing

  • Do not test for the presence of attributes in templates unless it provides some value. For example, attributes that are conditionally rendered or are important, such as ARIA attributes.