0.0.1 • Published 4 years ago

m2a-ui-library v0.0.1

Weekly downloads
-
License
MIT
Repository
-
Last release
4 years ago

The UI Library is framework agnostic. Components should be able to work in React, Vue, Angular or Vanilla Js.The UI should simply have Inputs which display information to the User.

Any Application should be a set of UI pages which have been built using the component library.

Components

The UI Library will have the following components.

Header

The purpose of the Header is to be a container for the Header child components.

Header Global

Header Global is a wrapper for central header item

Header Brand

Is used to house the Application brand item

Header Menu

Is for any dropdown/contextual user menus for the header.

Breadcrumbs

Is a breadcrumb container which spans full width of the application.

Breadcrumb is a single item which displays to the User the page location. The breadcrumb itemshould be clickable and take the User back to the previous routes.

Avatar

Is used to display a Users profile picture

Icon

Is a directive for the tag to display application Icons.

Button

Is a general purpose for add buttons.

Sidebar

Menu

MenuItem

DashPlayer

HLS Player

Shaka Player

AudioAnalyser

Footer

FooterItem

Table

Modal

ModalItem

Tabs

Tab Item

Accordion

Accordion Item

Purpose

The aim of this pattern library is to provide frontend developers accessible components and elements for building web applications.

All components will be thoroughly tested in the latest browser versions.

Users should use Git Flow for development.The main branch is Stable. All code should be deployed there.Users should not work in the Stable branch.Develop is the working branch, all Feature branches should branch from Develop.

Final build artifacts should be deployted to Master using git subtree

git subtree split --branch master --prefix dist/ng-galaxy/angular

This will run automatically after builds have successfully completed and passed all tests.

All builds must be tagged using Standard Version

Tooling

Node

Homebrew

NPM

Angular

Typescript

AWS

GraphQL

RXJS

NGRX Reactive

Storybook

Dash Player

Shaka Player

GitFlow

Standard Version

Working on new Features

Make sure you are on the develop branch and use Git Flow to create a new Feature Branch.

Git Flow essentials

git flow feature start

After build we need to deploy a subtree of the build folder to master

git subtree split --branch deploy --prefix dist/

Stencil

Stencil is a compiler for building fast web apps using Web Components.

Stencil combines the best concepts of the most popular frontend frameworks into a compile-time rather than run-time tool. Stencil takes TypeScript, JSX, a tiny virtual DOM layer, efficient one-way data binding, an asynchronous rendering pipeline (similar to React Fiber), and lazy-loading out of the box, and generates 100% standards-based Web Components that run in any browser supporting the Custom Elements v1 spec.

Stencil components are just Web Components, so they work in any major framework or with no framework at all.

Getting Started

To start building a new web component using m2a-ui-library, fork this repo to a new directory:

Then clone your fork, and add a local remote.

git clone <git clone git@bitbucket.org:{userName}/m2a-ui-library.git> cd m2a-ui-library git remote add username_local git@bitbucket.org:{userName}/m2a-ui-library.git

and run:

npm install npm start

To build the component for production, run:

npm run build

To run the unit tests for the components, run:

npm test

Need help? Check out our docs here.

File structure

One component per file.One component per directory. Though it may make sense to group similar components into the same directory, we've found it's easier to document components when each one has its own directory.Implementation (.tsx) and styles of a component should live in the same directory.

Example from m2a-ui-library:

├── assets ├── components │ ├── m2a-avatar │ │ ├── avatar.stories.js │ │ ├── m2a-avatar.scss │ │ ├── m2a-avatar.spec.ts │ │ ├── m2a-avatar.tsx │ │ └── readme.md │ ├── m2a-brand-logo │ │ ├── assets │ │ ├── brand.stories.js │ │ ├── m2a-brand-logo.d.ts │ │ ├── m2a-brand-logo.scss │ │ ├── m2a-brand-logo.tsx │ │ ├── m2a-brand.spec.ts │ │ └── readme.md │ ├── m2a-breadcrumb │ │ ├── breadcrumb.stories.js │ │ ├── breadcrumb.ts │ │ ├── m2a-breadcrumb.scss │ │ ├── m2a-breadcrumb.tsx │ │ └── readme.md │ ├── m2a-button │ │ ├── m2a-button.d.ts │ │ ├── m2a-button.scss │ │ ├── m2a-button.stories.js │ │ ├── m2a-button.tsx │ │ └── readme.md

Naming

HTML tagPREFIXThe prefix has a major role when you are creating a collection of components intended to be used across different projects, like @ionic/core. Web Components are not scoped because they are globally declared within the webpage, which means an "unique" prefix is needed to prevent collisions. The prefix is also able help to quickly identify the collection of an component. Additionally, web components are required to contain a "-" dash within the tag name, so using the first section to namespace your components is a natural fit.

We do not recommend using "stencil" as prefix, since Stencil DOES NOT emit stencil components, but rather the output is standards compliant web components.

DO NOT do this:

Instead, use your own naming or brand. For example, M2A components are all prefixed with ion-.

NAMEComponents are not actions, they are conceptually "things". It is better to use nouns instead of verbs, such as "animation" instead of "animating". "input", "tab", "nav", "menu" are some examples.

MODIFIERSWhen several components are related and/or coupled, it is a good idea to share the name, and then add different modifiers, for example:

Component (TS class)The name of the ES6 class of the component SHOULD NOT have a prefix since classes are scoped. There is no risk of collision.

@Component({ tag: 'm2a-button' }) export class Button { ... }

@Component({ tag: 'm2a-menu' }) export class Menu { ... }

TypeScript

Follow tslint-ionic-rules

Variable decorators should be inlined.

@Prop() name: string; @Element() el: HTMLElement; Method decorator should be multi-line @Listen('click') onClick() { ... }

Use private variables and methods as much possible: They are useful to detect deadcode and enforce encapsulation. Note that this is a feature which TypeScript provides to help harden your code, but using private, public or protected does not make a difference in the actual JavaScript output.

Code with Method/Prop/Event/Component decorators should have jsdocs: This allows for documentation generation and for better user experience in an editor that has TypeScript intellisense

Code organization

Newspaper Metaphor from The Robert C. Martin's Clean Code

The source file should be organized like a newspaper article, with the highest level summary at the top, and more and more details further down. Functions called from the top function come directly below it, and so on down to the lowest level and most detailed functions at the bottom. This is a good way to organize the source code, even though IDEs make the location of functions less important, since it is so easy to navigate in and out of them.

High level example (commented)

@Component({ tag: 'ion-something', styleUrls: { ios: 'something.ios.css', md: 'something.md.css', wp: 'something.wp.css' } }) export class Something {

/**

    1. Own Properties
  • Always set the type if a default value has not
  • been set. If a default value is being set, then type
  • is already inferred. List the own properties in
  • alphabetical order. Note that because these properties
  • do not have the @Prop() decorator, they will not be exposed
  • publicly on the host element, but only used internally. */ num: number; someText = 'default';

    /**

    1. Reference to host HTML element.
  • Inlined decorator */ @Element() el: HTMLElement;

    /**

    1. State() variables
  • Inlined decorator, alphabetical order. */ @State() isValidated: boolean; @State() status = 0;

    /**

    1. Public Property API
  • Inlined decorator, alphabetical order. These are
  • different than "own properties" in that public props
  • are exposed as properties and attributes on the host element.
  • Requires JSDocs for public API documentation. */ @Prop() content: string; @Prop() enabled: boolean; @Prop() menuId: string; @Prop() type = 'overlay';

    /**

  • Prop lifecycle events SHOULD go just behind the Prop they listen to.

  • This makes sense since both statements are strongly connected.
    • If renaming the instance variable name you must also update the name in @Watch()
    • Code is easier to follow and maintain. */ @Prop() swipeEnabled = true;

    @Watch('swipeEnabled') swipeEnabledChanged(newSwipeEnabled: boolean, oldSwipeEnabled: boolean) { this.updateState(); }

    /**

    1. Events section
  • Inlined decorator, alphabetical order.
  • Requires JSDocs for public API documentation. */ @Event() ionClose: EventEmitter; @Event() ionDrag: EventEmitter; @Event() ionOpen: EventEmitter;

    /**

    1. Component lifecycle events
  • Ordered by their natural call order, for example
  • WillLoad should go before DidLoad. */ componentWillLoad() {} componentDidLoad() {} componentWillUpdate() {} componentDidUpdate() {} componentDidUnload() {}

    /**

    1. Listeners
  • It is ok to place them in a different location
  • if makes more sense in the context. Recommend
  • starting a listener method with "on".
  • Always use two lines. */ @Listen('click', { enabled: false }) onClick(ev: UIEvent) { console.log('hi!') }

    /**

    1. Public methods API
  • These methods are exposed on the host element.
  • Always use two lines.
  • Public Methods must be async.
  • Requires JSDocs for public API documentation. */ @Method() async open(): Promise { ... return true; }

    @Method() async close(): Promise { ... }

    /**

    1. Local methods
  • Internal business logic. These methods cannot be
  • called from the host element. */ prepareAnimation(): Promise { ... }

    updateState() { ... }

    /**

    1. render() function
  • Always the last one in the class. */ render() { return ( <Host attribute="navigation" side={this.isRightSide ? 'right' : 'left'} type={this.type} class={{ 'something-is-animating': this.isAnimating }}
      >
        <div class='menu-inner page-inner'>
          <slot></slot>
        </div>
      </Host>
    );
    } }

Running unit tests

When creating new component tags, we recommend not using stencil in the component name (ex: ). This is because the generated component has little to nothing to do with Stencil; it's just a web component!

Instead, use a prefix that fits your company or any name for a group of related components. For example, all of the Ionic generated web components use the prefix ion.

StorybookJS

UI Library uses storybookjs for component documentation.

npm run storybook

Using this component

Script tag

Publish to NPM

Put a script tag similar to this > in the head of your index.html

Then you can use the element anywhere in your template, JSX, html etc

Node Modules

Run npm install my-component --save

Put a script tag similar to this in the head of your index.html

Then you can use the element anywhere in your template, JSX, html etc