Ember Freestyle is an Ember addon that allows you to quickly create a component explorer for your Ember app.

Github Actions Build Status NPM Package Version NPM Download Count Ember Observer Score


Live Demo

To see Ember Freestyle in action, visit the live demo here. Note: the acceptance tests run against this live demo.

Anatomy of a Basic Freestyle Guide

Here is a simple style guide, where <LoadingSpinner /> is a hypothetical component in your application.


  <FreestyleGuide @title="My Living Style Guide" @subtitle="Showcasing My App's Components">
    <FreestyleSection @name="UI Elements">
      <FreestyleUsage @slug="loading-spinner" @title="Loading Spinner">
        <LoadingSpinner />
      </FreestyleUsage>
    </FreestyleSection>
  </FreestyleGuide>
    

Components

Here's a brief rundown of the components Ember Freestyle provides for adding a living style guide in your app:

<FreestyleGuide>

The <FreestyleGuide> component provides the user interface for a style guide. It includes a header section and navigation controls.

<FreestyleUsage>

The <FreestyleUsage> component is the workhorse. Wrap your application's components with a <FreestyleUsage> component, being sure to provide a unique slug (positional param) as follows:


  <FreestyleUsage @slug="globally-unique-slug-1" @title="Title To Display In Style Guide">
    <Foo @propa="aaa" @propb="bbb" />
  </FreestyleUsage>
    

The snippet above will render your app's <Foo> component as well as a handlebars snippet demonstrating how to use it.

<FreestyleSection>

Optionally group your <FreestyleUsage>-wrapped components into sections using the <FreestyleSection> component. The <FreestyleSection> component registers itself in order to appear in the navigation provided by the <FreestyleGuide> component.

<FreestyleSubsection>

Optionally divide your style guide sections into subsections using the <FreestyleSubsection> component.


  <FreestyleGuide @title="My Living Style Guide" @subtitle="Showcasing My App's Components">
    <FreestyleSection @name="Visual Style" as |Section|>
      <Section.subsection @name="Typography">
        <FreestyleUsage @slug="visual-style-typography-foo" @title="Foo Typography">
          <FooTypography />
        </FreestyleUsage>
      </Section.subsection>

      <Section.subsection @name="Colors">
        <FreestyleUsage @slug="visual-style-colors-fie" @title='Fie Colors'>
          <FieColors />
        </FreestyleUsage>
      </Section.subsection>
    </FreestyleSection>
  </FreestyleGuide>
    

The snippet above will create a style guide with one 'Visual Style' section with separate subsections for 'Typography' and 'Colors'. Your app's <FooTypography> and <FieColors> components would show up in the appropriate subsections.

<FreestyleCollection> + <FreestyleVariant>

Use the <FreestyleCollection> component with nested <FreestyleVariant> components to present multiple versions of a component. This is very useful for presenting and testing a component in each state it must handle in your application.

By default, variants will be stacked. If you wish to view variants side by side, set the inline property of <FreestyleCollection> to true.


  <FreestyleCollection @title="Foo Component In Every State" @defaultKey="with-icon" @inline=true as |Collection|>
    <Collection.variant @key="no-num">
      <FreestyleUsage @slug="foo-foo-no-num" @title="Information">
        <FooFoo @title="Information" />
      </FreestyleUsage>
    </Collection.variant>

    <Collection.variant @key="with-num">
      <FreestyleUsage @slug="foo-foo-people" @title="People">
        <FooFoo @title="People" @num=55 />
      </FreestyleUsage>
    </Collection.variant>

    <Collection.variant @key="with-icon">
      <FreestyleUsage @slug="foo-foo-twitter" @title="Twitter">
        <FooFoo @title="Twitter" @icon="twitter" />
      </FreestyleUsage>
    </Collection.variant>
  </FreestyleCollection>
    

<FreestyleAnnotation>

Use the <FreestyleAnnotation> component to add documentation for a specific <FreestyleUsage>. Note that the <FreestyleAnnotation> slug must match the <FreestyleUsage> slug for filtering to work and in order to respect the Show Notes usage controls preference.


  <FreestyleUsage @slug="globally-unique-slug-2" @title="Title To Display In Style Guide">
    <Foo @propa="aaa" @propb="bbb" />
  </FreestyleUsage>

  <FreestyleAnnotation @slug="globally-unique-slug-2">
  <h1>Contextual HTML Note for Anything in the Freestyle Guide</h1>

  <p>
    You can write helpful HTML notes explaining anything in the
    Freestyle guide.
  </p>
  </FreestyleAnnotation>
    

<Freestyle::Usage> NEW!

We're excited about this approach to documenting components inspired by StoryBook DocBlocks. It's still evolving but we're using it quite happily.

For more information, check out the latest documentation.

Adding SASS and Javascript code snippets to Your Freestyle Guide

Previous versions of Freestyle had a built-in code snippet notation that allowed you to mark SASS, HBS or Javascript markup to display via freestyle-usage. The old style used "BEGIN-FREESTYLE-USAGE" and "END-FREESTYLE-USAGE" comments. This functionalty has been removed.

Instead, we recommend using the excellent ember-code-snippet addon. We have included an example of using this with some scss in the AlbumCover example in the tests/dummy/app/templates/acceptance.hbs and tests/dummy/app/styles/components/album-cover.scss source:

<FreestyleAnnotation @slug="album-cover">
  <h4>Styles for this component:</h4>
  {{#let (get-code-snippet "album-cover-styles.scss") as |snippet|}}
    <pre {{freestyle-highlight}} class="{{snippet.language}} u-outdent">{{snippet.source}}</pre>
  {{/let}}
</FreestyleAnnotation>

Adding markdown documentation to your Freestyle guide

Previous versions of Freestyle had a built-in freestyle-notes component that allowed you to write documentation in markdown. This functionality has been removed.

If you were not using markdown formatting in freestyle-note, we recommend using the freestyle-annotation component instead. Follow the freestyle-annoation usage guidance above.

If you were using markdown formatting, we recommend building your own markdown rendering component using a library such as remarkable. We have included an example of this approach in the AlbumCover example in the tests/dummy/app/templates/acceptance.hbs:

MarkdownContent Component Template

<div class="MarkdownContent" {{this.extractMarkdown}} ...attributes>
  <div class="MarkdownContent-raw">{{yield}}</div>
  {{!-- template-lint-disable no-triple-curlies --}}
  <div class="MarkdownContent-rendered">{{{this.renderedMarkdown}}}</div>
</div>

MarkdownExample Component

import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { Remarkable } from 'remarkable';
import stripIndent from 'strip-indent';
import { modifier } from 'ember-modifier';

interface Signature {
  Element: HTMLDivElement;
  Blocks: {
    default: [];
  };
}

export default class MarkdownContent extends Component<Signature> {
  @tracked rawMarkdown: string | undefined;

  extractMarkdown = modifier((element: HTMLElement): void => {
    this.rawMarkdown = element.querySelector('.MarkdownContent-raw')?.innerHTML;
  });

  get renderedMarkdown(): string | null {
    if (this.rawMarkdown) {
      return new Remarkable().render(stripIndent(this.rawMarkdown));
    }
    return null;
  }
}

MarkdownContent Component Usage

<FreestyleAnnotation @slug="album-cover">
  <MarkdownContent>
    ## Album Review

    I really love this album. Ringo at his finest. Here are things I like about it:

    - Submarines
    - Psychadelica
    - The color yellow
  </MarkdownContent>
</FreestyleAnnotation>

Showing/Hiding the "All" components view

You might find that rendering all your freestyle components in one page isn't desirable. The page might run slow, or you might want to encourage developers to use sub-sections. To hide the all menu option, and set the ember-freestyle service's `allowRenderingAllSections` to false.

app/routes/application.js


  import Route from '@ember/routing/route';
  import { inject as service } from '@ember/service';

  export default class ApplicationRoute extends Route {
    @service emberFreestyle;

    beforeModel() {
      this.emberFreestyle.allowRenderingAllSections = false;
    }
  }
    

Removing Ember Freestyle from Your Production Payload

We recommend excluding Ember Freestyle from production builds using Ember CLI's `addons.exclude` option.


  // ember-cli-build.js

  const environment = process.env.EMBER_ENV;
  const addonsToExclude = environment === 'production' ? ['ember-freestyle'] : [];

  module.exports = function (defaults) {
    const app = new EmberApp(defaults, {
      addons: {
        exclude: addonsToExclude,
      },
    };
  };
    

Living Style Guide Driven Development

Ember Freestyle facilitates a living style guide driven development approach for Ember apps. Chris LoPresto gave a talk on this topic at EmberConf 2016. Interactive Slides

Acknowledgements

Ember Freestyle would not be possible without inspiration from the Ember community at large. Specific thanks go out to:

Getting Started

Installation

This installation process is opinionated in order to get you going quickly. We are currently working to make it much easier to configure Ember Freestyle. Please report any problems, and as always, PRs are welcome.

Installing Ember Freestyle


  ember install ember-freestyle
    

This will do the following:

Note: Ember CLI versions before 0.2.3 should use ember install:addon instead of ember install

Setup

Routing the Generated Freestyle Guide

If you said yes to the prompts from the generator above, you are almost ready to display a Freestyle Guide in your app.

  1. Add this.route('freestyle'); to your router.js file
  2. Navigate to /freestyle.

Note: Ember Freestyle is compatible with two trailing Ember LTS releases.