New
Storybook for React Native (6.5)Automate with Chromatic
Storybook Day 2023
Star77,752
Back to Create an Addon
React
Chapters
  • 소개
  • 설치
  • 상태 추적하기
  • 데코레이터
  • 프리셋
  • 카탈로그에 추가하세요
  • 결론

데코레이터

스토리와의 상호작용

이제 거의 끝났습니다. 지금까지 우리는 상태를 추적할 수 있는 툴을 만들어 툴바에 추가했고, 이제는 이 상태에 대응하여 아웃라인을 표시하고, 숨겨야 합니다.

데코레이터는 스토리를 감싸고 엑스트라 렌더링 기능을 추가합니다. 이번 장에서 글로벌 아웃라인에 대응하고 CSS 인젝션을 처리하는 데코레이터를 만들어 볼 것입니다. 차례로 모든 HTML 요소 주위에 아웃라인을 그려볼 것입니다.

이전 단계에서 outlineActive을 전역으로 정의해봤습니다. 이어서 useGlobals 훅을 사용하여 데코레이터에서 전역 상태를 사용할 수 있습니다.

Copy
src/withGlobals.js
/* eslint-env browser */
import { useEffect, useGlobals } from '@storybook/addons';

export const withGlobals = (StoryFn, context) => {
  const [{ outlineActive }, updateGlobals] = useGlobals();
  // Is the addon being used in the docs panel
  const isInDocs = context.viewMode === 'docs';

  useEffect(() => {
    // Execute your side effect here
    // For example, to manipulate the contents of the preview
    const selectorId = isInDocs ? `#anchor--${context.id} .docs-story` : `root`;

    displayToolState(selectorId, { outlineActive, isInDocs });
  }, [outlineActive]);

  return StoryFn();
};

function displayToolState(selector, state) {
  const rootElement = document.getElementById(selector);
  let preElement = rootElement.querySelector('pre');

  if (!preElement) {
    preElement = document.createElement('pre');
    preElement.style.setProperty('margin-top', '2rem');
    preElement.style.setProperty('padding', '1rem');
    preElement.style.setProperty('background-color', '#eee');
    preElement.style.setProperty('border-radius', '3px');
    preElement.style.setProperty('max-width', '600px');
    rootElement.appendChild(preElement);
  }

  preElement.innerText = `This snippet is injected by the withGlobals decorator.
It updates as the user interacts with the ⚡ tool in the toolbar above.
${JSON.stringify(state, null, 2)}
`;
}

아웃라인 CSS 삽입하기

스타일을 추가하거나 제거하는 것은 부수 작용(side effect)이기 때문에 이 작업을 useEffect에서 함수 안에 감싸줘야 합니다. 다음으로 outlineActive 전역에 의해 트리거됩니다. Kit 코드는 예시가 제공되지만, 아웃라인 CSS 주입을 처리하도록 업데이트 해보겠습니다.

Copy
src/withGlobals.js
/* eslint-env browser */
import { useEffect, useMemo, useGlobals } from '@storybook/addons';

import { clearStyles, addOutlineStyles } from './helpers';
import outlineCSS from './outlineCSS';

export const withGlobals = (StoryFn, context) => {
  const [{ outlineActive }, updateGlobals] = useGlobals();
  // Is the addon being used in the docs panel
  const isInDocs = context.viewMode === 'docs';

  const outlineStyles = useMemo(() => {
    const selector = isInDocs ? `#anchor--${context.id} .docs-story` : '.sb-show-main';

    return outlineCSS(selector);
  }, [context.id]);

  useEffect(() => {
    const selectorId = isInDocs ? `my-addon-outline-docs-${context.id}` : `my-addon-outline`;

    if (!outlineActive) {
      clearStyles(selectorId);
      return;
    }

    addOutlineStyles(selectorId, outlineStyles);

    return () => {
      clearStyles(selectorId);
    };
  }, [outlineActive, outlineStyles, context.id]);

  return StoryFn();
};

좋습니다, 큰 도약을 한 것 같습니다. 모든 변경사항을 살펴보겠습니다.

애드온은 문서 및 스토리 보기 모드에서 모두 활성화될 수 있습니다. 미리 보기 iframe의 실제 DOM 노드는 이 두 모드에서 서로 다릅니다. 실제로, 문서 모드는 한 페이지에 여러 개의 스토리 미리보기를 렌더링합니다. 따라서 스타일이 주입될 DOM 노드에 적합한 셀렉터(selector)를 선택해야 합니다. 또한 CSS는 특정 셀렉터(selector)로 범위를 지정해야 합니다.

💡 여기서 useMemouseEffect는 리액트가 아니라 @storybook/addons에서 가져온 것입니다. 왜냐하면 데코레이터 코드가 스토리북(Storybook) 미리보기에서 실행되기 때문입니다. 리액트를 포함하지 않을 사용자 코드가 불러와지는 곳이기 때문에 스토리북은 우리가 사용하는 리액트와 유사한 훅 라이브러리를 구현합니다.

다음으로, DOM에 스타일을 주입할 때, 사용자가 스타일을 끄거나 보기 모드를 전환할 때 스타일을 초기화 할 수 있도록 스타일을 추적해야 합니다.

이 모든 CSS 로직을 관리하려면 헬퍼 함수가 필요합니다. 이 함수는 DOM API를 사용하여 스타일시트를 주입하고 제거합니다.

Copy
src/helpers.js
/* eslint-env browser */
export const clearStyles = selector => {
  const selectors = Array.isArray(selector) ? selector : [selector];
  selectors.forEach(clearStyle);
};

const clearStyle = selector => {
  const element = document.getElementById(selector);
  if (element && element.parentElement) {
    element.parentElement.removeChild(element);
  }
};

export const addOutlineStyles = (selector, css) => {
  const existingStyle = document.getElementById(selector);
  if (existingStyle) {
    if (existingStyle.innerHTML !== css) {
      existingStyle.innerHTML = css;
    }
  } else {
    const style = document.createElement('style');
    style.setAttribute('id', selector);
    style.innerHTML = css;
    document.head.appendChild(style);
  }
};

그리고 CSS의 outline 자체는 Pesticide가 사용하는 것을 기반으로 합니다. outlineCSS.js 파일에서 가져오세요.

이를 통해 UI 요소 주위에 outline을 그릴 수 있습니다.

도구를 껐다 켰다 하면 아웃라인이 나타납니다

Keep your code in sync with this chapter. View 0e7246a on GitHub.
Is this free guide helping you? Tweet to give kudos and help other devs find it.
Next Chapter
프리셋
모든 스토리에 아웃라인을 보여줄 수 있습니다.
✍️ Edit on GitHub – PRs welcome!
Join the community
6,075 developers and counting
WhyWhy StorybookComponent-driven UI
Open source software
Storybook

Maintained by
Chromatic
Special thanks to Netlify and CircleCI