Skip to content

Component creation

Marat Zimnurov edited this page Aug 30, 2020 · 2 revisions

Best practice

  • components/component/

    • Component.stories.ts (describe usage of the component for Storybook)
    • Component.utils.ts (all logic & types for the component)
    • Component.svelte (basic view of the component)
    • Component.scss / Component.css (style of component)
    • Component.spec.ts (tests for Component.utils.ts)
  • Order of imports/export (ASC sorting)

    • export props
    • Svelte-library imports
    • External libraries
    • Type .ts
    • Logic .ts
    • Logic .js
    • Components .svelte
    • Style .scss/.css
    • local variables
    • local functions
    • Svelte lifecycle hooks

example:

  export let description:string
  export let uuid:string

  import { onMount } from 'svelte'
  import { writable, Writable } from 'svelte/store'

  import _ from 'lodash'
  import fetch from 'cross-env'
  
  import type Post from '../../interfaces/post.interface'

  import Request from '../../classes/request'
  import { HOST } from '../../helpers/environment.helper'
  import { loggerWithDate } from '../../helpers/logger.helper'

  import Content from '../../components/content/Content.svelte'
  import GlobalHeader from '../../components/header/GlobalHeader.svelte'
  import Header from '../../components/header/Header.svelte'

  import './Style.css'
  import './Style.scss'

  const store: Writable<boolean> = writable(false)
  
  const isDisabled = () => !$store
  const isEnabled = () => $store

  onDestroy(() => {
    if (isDisabled()) {
      console.log('disabled')
    })
  })

  onMount(() => {
    if (isEnabled()) {
      console.log('enabled')
    }
  })

Create a component

<!-- Image.svlete -->

<script>
  export let alt: string
  export let caption: string
  export let height: string
  export let src: string
  export let width: string

  export let onCaptionClick: () => void
  export let onClick: () => void

  import { onMount } from 'svelte'

  import './Image.scss'

  onMount(() => {
    isNoEmptyAlt(alt)
  })
</script>

<figure>
  <img on:click="{onClick}" {alt} {height} {src} {width} />

  {#if caption}
    <figcaption on:click="{onCaptionClick}">{caption}</figcaption>
  {/if}
</figure>

Describe all logic

// Image.utils.ts

export type ImageProps = {
  alt: string
  caption?: string
  height: string
  src: string
  width: string

  onCaptionClick?: () => void
  onClick?: () => void
}

export const initialState: ImageProps = {
  alt: 'logo',
  src: 'assets/img/logo.png',
  height: '100%',
  width: '100%',
}

export const initalStoryState: ImageProps = {
  ...initialState,
  src: 'https://github.com/Zimtir/SENT-template/blob/master/public/assets/img/logo.png?raw=true',
}

export const isNoEmptyAlt = (alt: string): boolean => {
  if (alt && alt.length > 0 && alt !== '') {
    return true
  }
  return false
}

Create a story

// Image.stories.ts

import { withKnobs } from '@storybook/addon-knobs'

import { wrap, ComponentStory } from '../../helpers/storybook.helper'
import type { ImageProps, initialStoryState } from './Image.utils'

import Image from './Image.svelte'

export const Default = (): ComponentStory<ImageProps> => wrap<ImageProps>('image', Image, {
  ...initialStoryState,
  onCaptionClick: action('onCaptionClick'),
  onClick: action('onClick'),
})

export default {
  title: 'Image',
  decorators: [withKnobs],
}

Create a styles

// All variables placed at src/styles/variables.scss
// Import of variables will be executed automatically 

// Image.scss 

figure {
  text-align: center;
  margin: $width-margin;

  img {
    margin: $soft-margin;
  }
}

Create a tests

Use one of the following commands to execute tests:

# :dev and :prod - will start a dev server before a test execution
# headless - just output to console
# ui - client to control a test execution

npm run test:headless
npm run test:headless:dev
npm run test:headless:prod

npm run test:ui
npm run test:ui:dev
npm run test:ui:prod

# example of specific rule 
npm run test:headless Image.spec.ts
// example of unit test

import { isNoEmptyAlt, initialState } from './Image.utils'

describe('units', () => {
  it('isNoEmptyAlt returns true for a not empty alt', () => {
    // Arrange
    const expected = true

    // Actual
    const actual = isNoEmptyAlt('alt')

    // Assert
    expect(actual).equal(expected)
  })
})

// example of integration test

describe('integrations', () => {
  beforeEach(() => {
    cy.visit('/')
  })

  it('has the correct alt for <img>', () => {
    cy.get('img').invoke('attr', 'alt').should('eq', initialState.alt)
  })
})
Clone this wiki locally