๋ฒ๊ทธ ์ค๋ช
๋ชจ๋ Storybook Iframe์ ๋ํด ๊ธ๋ก๋ฒ ์คํ์ผ (scss)์ ํ ๋ฒ ํตํฉํ๋ ์ฌ๋ฌ ๋ฐฉ๋ฒ์ ์๋ํ์ง๋ง ์คํจํ์ต๋๋ค. ์ด ์์
์ ์ํํ๋ ๋ช
ํํ๊ณ ๊ฐ๊ฒฐํ ๋ฐฉ๋ฒ์ด์๋ ๊ฒ ๊ฐ์ต๋๋ค.
์ฌํํ๋ ค๋ฉด
์๋ :
์์๋๋ ํ๋
๋ชจ๋ ์คํ ๋ฆฌ์ Iframe์ ์ ์ฉ ํ ์ ์๋๋ก ๊ธ๋ก๋ฒ (scss) ํ์ผ์ ํ ๊ณณ์์ ๊ฐ์ ธ์ฌ ์ ์๊ธฐ๋ฅผ ๊ธฐ๋ํฉ๋๋ค. ๋ด๊ฐ ์์ง ๋ชปํ๋ ๊ตฌ์ฑ ์ค์ ์ด์์ ์ ์์ต๋๊น?
์ฒด๊ณ:
์ถ๊ฐ ์ปจํ
์คํธ
์ด ๋ฌธ์ ๋ฅผ ์ฝ๊ฒ ํด๊ฒฐํ ์์๋ ์คํ ๋ฆฌ ๋ถ์ ์ผ๋ถ ๊ตฌ์ฑ์ด ๋๋ฝ๋๊ธฐ๋ฅผ ๋ฐ๋๋๋ค.
global.scss ํ์ผ์ angular.json
์ถ๊ฐํด๋ณด์ญ์์ค.
defaultProject (json ๋ด๋ถ์ defaultProject ์์ฑ์ด ์์)๋ฅผ ํ์ธํ๊ณ ์คํ์ผ ๋ชฉ๋ก์ ์ถ๊ฐํฉ๋๋ค.
"styles": [
"projects/your-cli-project/src/lib/styles/your-styles.scss"
],
๋๋ ๊ทธ๋ ๊ฒํ๊ณ ์ ์๋ํฉ๋๋ค. ๊ทธ๊ฒ ๋น์ ์์ํ ์ ํ์ธ๊ฐ์?
์๋ ํ์ธ์ ์ฌ๋ฌ๋ถ! ์ต๊ทผ์ด ๋ฌธ์ ์์ ๋ง์ ์ผ์ด ์ผ์ด๋์ง ์์ ๊ฒ ๊ฐ์ต๋๋ค. ์ฌ์ ํ ์ง๋ฌธ, ์๊ฒฌ ๋๋ ๋ฒ๊ทธ๊ฐ ์์ผ๋ฉด ํ ๋ก ์ ๊ณ์ํ์ญ์์ค. ์ํ๊น๊ฒ๋ ๋ชจ๋ ๋ฌธ์ ๋ฅผ ๋ค๋ฃฐ ์๊ฐ์ด ์์ต๋๋ค. ์ฐ๋ฆฌ๋ ํญ์ ๊ธฐ์ฌ์ ์ด๋ ค ์์ผ๋ฏ๋ก ๋์์ด ํ์ํ๋ฉด ํ ์์ฒญ์ ๋ณด๋ด์ฃผ์ญ์์ค. ๋นํ์ฑ ๋ฌธ์ ๋ 30 ์ผ ํ์ ๋ง๊ฐ๋ฉ๋๋ค. ๊ฐ์ฌ!
์๋
ํ์ธ์ @omaracrystal ์ ์ ๊ฐ ํ ๋ฐฉ๋ฒ์
๋๋ค.
.storybook/config.js
ํ์ผ์์ ์ฌ๋ฐ๋ฅธ ๋ก๋๋ฅผ ์ธ๋ผ์ธ์ผ๋ก ๊ฐ์ ธ ์ค๊ธฐ๋ฅผ ์ถ๊ฐํ์ญ์์ค.
import '!style-loader!css-loader!sass-loader!./scss-loader.scss';
๊ทธ๋ฐ ๋ค์ scss-loader.scss
ํ์ผ์์ ํ์ํ ๋ชจ๋ ์คํ์ผ์ ์ถ๊ฐ / ๊ฐ์ ธ์ต๋๋ค.
$font-path: '../projects/lib/src/theming/fonts/OpenSans/';
<strong i="13">@import</strong> '../projects/lib/src/theming/reset.scss';
<strong i="14">@import</strong> '../projects/lib/src/theming/main.scss';
html {
font-family: $font-name;
font-size: $font-size--small;
}
@kroeder ๊ฐ ์ ์ํ ์๋ฃจ์
์ Angular ์์ฉ ํ๋ก๊ทธ๋จ์ ๊ตฌ์ฑ ์์๋ฅผ ์ฌ์ฉํ๋ ํ ์๋ํ์ง๋ง ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๊ฒฝ์ฐ angular.json ํ์ผ์ styles
์์ฑ์ ์ถ๊ฐ ํ ์ ์์ต๋๋ค.
global.scss ํ์ผ์
angular.json
์ถ๊ฐํด๋ณด์ญ์์ค.defaultProject (json ๋ด๋ถ์ defaultProject ์์ฑ์ด ์์)๋ฅผ ํ์ธํ๊ณ ์คํ์ผ ๋ชฉ๋ก์ ์ถ๊ฐํฉ๋๋ค.
"styles": [ "projects/your-cli-project/src/lib/styles/your-styles.scss" ],
๋๋ ๊ทธ๋ ๊ฒํ๊ณ ์ ์๋ํฉ๋๋ค. ๊ทธ๊ฒ ๋น์ ์์ํ ์ ํ์ธ๊ฐ์?
์ด ์์ ์ ์ ํํ ์ํํ์ง๋ง ์คํ ๋ฆฌ ๋ถ์ ์คํํ ๋๋ก๋๋์ง ์๋ ๊ฒ ๊ฐ์ต๋๋ค.
์๋ ํ์ธ์ ์ฌ๋ฌ๋ถ! ์ต๊ทผ์ด ๋ฌธ์ ์์ ๋ง์ ์ผ์ด ์ผ์ด๋์ง ์์ ๊ฒ ๊ฐ์ต๋๋ค. ์ฌ์ ํ ์ง๋ฌธ, ์๊ฒฌ ๋๋ ๋ฒ๊ทธ๊ฐ ์์ผ๋ฉด ํ ๋ก ์ ๊ณ์ํ์ญ์์ค. ์ํ๊น๊ฒ๋ ๋ชจ๋ ๋ฌธ์ ๋ฅผ ๋ค๋ฃฐ ์๊ฐ์ด ์์ต๋๋ค. ์ฐ๋ฆฌ๋ ํญ์ ๊ธฐ์ฌ์ ์ด๋ ค ์์ผ๋ฏ๋ก ๋์์ด ํ์ํ๋ฉด ํ ์์ฒญ์ ๋ณด๋ด์ฃผ์ญ์์ค. ๋นํ์ฑ ๋ฌธ์ ๋ 30 ์ผ ํ์ ๋ง๊ฐ๋ฉ๋๋ค. ๊ฐ์ฌ!
์๋ , ๋ ๋์ผ! ์ด ๋ฌธ์ ๋ฅผ ๋ง๋ฌด๋ฆฌํ์ฌ ๊ด๋ฆฌ์๊ฐ ๋์ ํ์ฌ ๊ฐ๋ฐ ๋ก๋๋งต์ ์ง์คํ ์ ์๋๋กํ๊ฒ ์ต๋๋ค. ์ธ๊ธ ๋ ๋ฌธ์ ๊ฐ ์ฌ์ ํ ์ฐ๋ ค๋๋ ๊ฒฝ์ฐ ์ ํฐ์ผ์ ์ด๊ณ ์ด ์ด์ ํฐ์ผ์ ์ธ๊ธํ์ญ์์ค. ์คํ ๋ฆฌ ๋ถ์ ์ฌ์ฉํด ์ฃผ์ ์ ๊ฐ์ฌํฉ๋๋ค!
์๋ ํ์ธ์ @omaracrystal ์ ์ ๊ฐ ํ ๋ฐฉ๋ฒ์ ๋๋ค.
.storybook/config.js
ํ์ผ์์ ์ฌ๋ฐ๋ฅธ ๋ก๋๋ฅผ ์ธ๋ผ์ธ์ผ๋ก ๊ฐ์ ธ ์ค๊ธฐ๋ฅผ ์ถ๊ฐํ์ญ์์ค.import '!style-loader!css-loader!sass-loader!./scss-loader.scss';
๊ทธ๋ฐ ๋ค์
scss-loader.scss
ํ์ผ์์ ํ์ํ ๋ชจ๋ ์คํ์ผ์ ์ถ๊ฐ / ๊ฐ์ ธ์ต๋๋ค.$font-path: '../projects/lib/src/theming/fonts/OpenSans/'; <strong i="14">@import</strong> '../projects/lib/src/theming/reset.scss'; <strong i="15">@import</strong> '../projects/lib/src/theming/main.scss'; html { font-family: $font-name; font-size: $font-size--small; }
@kroeder ๊ฐ ์ ์ํ ์๋ฃจ์ ์ Angular ์์ฉ ํ๋ก๊ทธ๋จ์ ๊ตฌ์ฑ ์์๋ฅผ ์ฌ์ฉํ๋ ํ ์๋ํ์ง๋ง ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๊ฒฝ์ฐ angular.json ํ์ผ์
styles
์์ฑ์ ์ถ๊ฐ ํ ์ ์์ต๋๋ค.
๋ชจ๋์๊ฒ ํจ๊ณผ๊ฐ ์์ต๋๊น? ๋ด ํธ์ด ์๋๋๋ค :(
@ozanmanav .storybook/config.js
์์ ๊ฐ์ ธ ์ค๋ ค๊ณ ํ์ต๋๊น?
์ถ์ : ์ ์๊ฒ๋ ์๋ํ์ง๋ง webpack-loaders์์ด .css
ํ์ผ ๋ง ๊ฐ์ ธ ์ค๊ธฐ ๋๋ฌธ์
๋๋ค.
Storybook v5.3์ main.js ํ์ผ ๊ตฌ์กฐ๋ฅผ ์ฌ์ฉํ๋ฉด ๋ ์ด์ ์๋ํ์ง ์๋ ๊ฒ ๊ฐ์ต๋๋ค.
@garrettmaring ๋น์ ๊ณผ ํจ๊ป ์๋ ์์๋ค .storybook/preview.js
๋ณด๋ค๋ .storybook/config.js
?
์๋
ํ์ธ์.
๋ง์นจ๋ด import '!style-loader!css-loader!./main.css'
์ฌ์ฉํ์ฌ .css
ํ์ผ์์ ์๋ํ๋๋กํ์ต๋๋ค.
์ฒ์์๋ webpack ๊ตฌ์ฑ์ ์๋ํ์ง๋ง ๋ชจ๋ ์ฌ์ฉ์ ์ง์ ๊ท์น์ ์ ๊ฑฐํ๊ณ ์์ ๊ฐ์ด ์ธ๋ผ์ธํ์ต๋๋ค. ๋ถ๋ช
ํ npm์ ๋ก๋๋ฅผ ์ค์นํฉ๋๋ค.
@shilman ์, ๊ทธ๋ฌ์ต๋๋ค ๐ ๋ช ๊ฐ์ง ๋ฌธ์๋ฅผ ๋ณด์์ ๋ ๊ฝค ๋ถ๋ช ํฉ๋๋ค. ๋ฉ์ธ ์คํ ๋ฆฌ ๋ถ ๋ฌธ์์ ๋ ๋ช ํํ ๋ฌธ์๋ฅผ์ํ ๊ณต๊ฐ์ด์์ ์ ์์ต๋๋ค. ์ด ๊ธฐ์ฌ ๊ฐ ๋ช ํํ๋ค๋ ๊ฒ์ ์์์ต๋๋ค.
@garrettmaring ๋ฌธ์๋ฅผ ๊ฐ์ ํ๋ ๋ฐ ๋์์ด๋๋๋ก PR์ ์ ์ถํ๊ณ ์์ต๋๊น?
์๋ ํ์ธ์.
๋ง์นจ๋ดimport '!style-loader!css-loader!./main.css'
์ฌ์ฉํ์ฌ.css
ํ์ผ์์ ์๋ํ๋๋กํ์ต๋๋ค.
์ฒ์์๋ webpack ๊ตฌ์ฑ์ ์๋ํ์ง๋ง ๋ชจ๋ ์ฌ์ฉ์ ์ง์ ๊ท์น์ ์ ๊ฑฐํ๊ณ ์์ ๊ฐ์ด ์ธ๋ผ์ธํ์ต๋๋ค. ๋ถ๋ช ํ npm์ ๋ก๋๋ฅผ ์ค์นํฉ๋๋ค.
๋ด๊ฐ ๋ฐฉ๊ธ ์์ ๋ธ ์ฐธ๊ณ ๋ก์ด ์ ๊ทผ ๋ฐฉ์์ ๋ก์ปฌ์์ ์คํ ๋ฆฌ ๋ถ์ ์ ๊ณต ํ ๋ ๊ฒฝ์ด๋ก์ธ ์ ์์ง๋ง ๋น๋ ์คํ ๋ฆฌ ๋ถ์์๋ ์๋ํ์ง ์์ต๋๋ค (๋ด๊ฐ ๋ณธ ๊ฒ์์).
๋ด ์๋ฃจ์
preview-head.html์ <link rel="stylesheet" href="./your-global-styles.css" />
์ ์ถ๊ฐํฉ๋๋ค. ๊ทธ๋ฐ ๋ค์ your-global-styles.css
๋๋ ํ ๋ฆฌ์ -s ํ๋๊ทธ๋ฅผ ์ฌ์ฉํ์ฌ ์คํ์ผ์ ์คํ ๋ฆฌ ๋ถ์ ๋น๋ ๋๋ ํ ๋ฆฌ์ ๋ณต์ฌํฉ๋๋ค. ์ด์ iframe.html์ ๋์ผํ ๋๋ ํ ๋ฆฌ์์ your-global-styles.css
๋ฅผ "์ฐธ์กฐํด์ผํฉ๋๋ค".
์ด ๋ฌธ์ ๋ ๋ก์ปฌ์์ ์คํํ ๋๋ง ๋ธ๋ผ์ฐ์ ์ฝ์์์ localhost/your-global-styles.css
์ฐพ์ ์ ์๋ค๊ณ ํ์๋์ง๋ง ์ฌ์ ํ ์๋ ํ๋๋ก ์๋ํฉ๋๋ค.
๋๊ตฌ๋ ์ง ์ด๊ฒ์ ์ ๊ทผํ๋ ๋ ์ข์ ๋ฐฉ๋ฒ์ด ์๊ฑฐ๋ ์ ์ ํ ํด๊ฒฐ์ฑ ์ ์๊ณ ์๋ค๋ฉด ์๋ ค์ฃผ์ญ์์ค :)
๋ค์์ ์ฌ์ฉํ์ฌ ์คํ ๋ฆฌ ๋ถ์ scss ํ์ผ์ ์ ๊ณตํฉ๋๋ค.
import '! style-loader! css-loader! sass-loader! ./ main.scss';
์ด๊ฒ์ ์ฐ๋ฆฌ์๊ฒ ์ ์๋ํฉ๋๋ค.
@blemaire ์ด๋์์ ๊ทธ ๋ผ์ธ์ ์ฌ์ฉํ๊ณ ์์ต๋๊น?
@blemaire ์ด๋์์ ๊ทธ ๋ผ์ธ์ ์ฌ์ฉํ๊ณ ์์ต๋๊น?
@lopis ๋ .storybook
ํด๋์ preview.js
๋ฅผ ๋ง๋ค๊ณ ๊ทธ ์ค์ ๊ทธ ์์ ๋ฃ์ต๋๋ค.
๋ชจ๋ ๊ฐ์ฌํฉ๋๋ค. ํ์ธ
.storybook
(๋๋ ์ํ๋ ์์น)์ scss ํ์ผ์ ์ถ๊ฐํฉ๋๋ค..storybook/preview.js
๋ง๋๋ ์คpreview.js
๋๋ ํ ๋ฆฌ์ import '!style-loader!css-loader!sass-loader!./styles.scss';
์ถ๊ฐ"css-loader"
์ devDependency๋ก package.json
์ฐ๋ฆฌ๋ฅผ ์ํด ์ผํฉ๋๋ค. Storybook icw an Angular 9 ๋ผ์ด๋ธ๋ฌ๋ฆฌ ( angular.json
ํ์ผ์ ์ถ๊ฐํ๋ ์ต์
์์)
nextjs ๊ธฐ๋ฐ ์ค์ ์์ import "../styles/main.css";
(๋ฉ์ธ ์ฑ์์์ ๊ฐ์ด)์ ๊ฐ๋จํ ์ค์ .storybook/preview.js
์คํ์ผ์ ์ฌ๋ฐ๋ฅด๊ฒ ์ถ๊ฐํฉ๋๋ค.
์์ ์๋ฃจ์
์ ๋ชจ๋ ๊ฒฝ๋ก๊ฐ ์๋์ ์ธ ../../src/main.scss
์ด์ง๋ง ํ์ฌ ํ๋ก์ ํธ์์ SCSS ์ ์ญ / ๋ณ์นญ์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ ์๋ํฉ๋๋ค.
Storybook์ ๋ํ ๋ด main.js
์นํฉ ๊ตฌ์ฑ์ด ๋ณ์นญ์ ์ปดํ์ผ ํ ์ ์์ต๋๋ค. ํ์ง๋ง ์ ์ญ / ๋ณ์นญ SCSS๋ฅผ ๋ด preview.js
๋ก ๊ฐ์ ธ ์ค๋ ค๊ณ ํ๋ฉด ๊ฒฝ๋ก๋ฅผ ํ์ธํ ์ ์์ต๋๋ค.
์ด๋๋ก ๊ฐ์ผํ ์ง ๋ชจ๋ฅด๊ฒ ์ต๋๋ค.
import '!style-loader!css-loader!sass-loader!./scss-loader.scss';
๋๋ ์ด๊ฒ์ ์๋ํ์ง๋ง ์ค๋ฅ๊ฐ ๋ฐ์ํฉ๋๋ค. ๋ด ์ ์ญ CSS ๋ณ์์ ๋ฏน์ค ์ธ์ ๋ํด ์๊ธฐ ์ํด Storybook์ด ํ์ํฉ๋๋ค.
ERR! import '!style-loader!css-loader!sass-loader!./scss-loader.scss';
ERR! ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ERR!
ERR! SyntaxError: Unexpected string
nextjs ๊ธฐ๋ฐ ์ค์ ์์
import "../styles/main.css";
(๋ฉ์ธ ์ฑ์์์ ๊ฐ์ด)์ ๊ฐ๋จํ ์ค์.storybook/preview.js
์คํ์ผ์ ์ฌ๋ฐ๋ฅด๊ฒ ์ถ๊ฐํฉ๋๋ค.
์ด styles / main.css์ ์ ์ญ SCSS ๋ณ์๊ฐ ํฌํจ๋์ด ์์ต๋๊น? ์คํ ๋ฆฌ ๋ถ์ ์ ์ฉ ๋๋์? ์๋ํ์ง๋ง ์ฌ์ ํ SassError: Undefined variable: $my-variable-here
์๋ ํ์ธ์ @omaracrystal ์ ์ ๊ฐ ํ ๋ฐฉ๋ฒ์ ๋๋ค.
.storybook/config.js
ํ์ผ์์ ์ฌ๋ฐ๋ฅธ ๋ก๋๋ฅผ ์ธ๋ผ์ธ์ผ๋ก ๊ฐ์ ธ ์ค๊ธฐ๋ฅผ ์ถ๊ฐํ์ญ์์ค.import '!style-loader!css-loader!sass-loader!./scss-loader.scss';
๊ทธ๋ฐ ๋ค์
scss-loader.scss
ํ์ผ์์ ํ์ํ ๋ชจ๋ ์คํ์ผ์ ์ถ๊ฐ / ๊ฐ์ ธ์ต๋๋ค.$font-path: '../projects/lib/src/theming/fonts/OpenSans/'; <strong i="15">@import</strong> '../projects/lib/src/theming/reset.scss'; <strong i="16">@import</strong> '../projects/lib/src/theming/main.scss'; html { font-family: $font-name; font-size: $font-size--small; }
@kroeder ๊ฐ ์ ์ํ ์๋ฃจ์ ์ Angular ์์ฉ ํ๋ก๊ทธ๋จ์ ๊ตฌ์ฑ ์์๋ฅผ ์ฌ์ฉํ๋ ํ ์๋ํ์ง๋ง ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๊ฒฝ์ฐ angular.json ํ์ผ์
styles
์์ฑ์ ์ถ๊ฐ ํ ์ ์์ต๋๋ค.๋ชจ๋์๊ฒ ํจ๊ณผ๊ฐ ์์ต๋๊น? ๋ด ํธ์ด ์๋๋๋ค :(
์๋. ๋๋ ๋์์ด๋์ง ์์ต๋๋ค. ์ด์ ์ด ์ค๋ฅ๊ฐ ๋ฐ์ํฉ๋๋ค.
ERR! import '!style-loader!css-loader!sass-loader!./scss-loader.scss';
ERR! ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ERR!
ERR! SyntaxError: Unexpected string
๋ฒ๊ทธ ์ค๋ช
๋ชจ๋ Storybook Iframe์ ๋ํด ๊ธ๋ก๋ฒ ์คํ์ผ (scss)์ ํ ๋ฒ ํตํฉํ๋ ์ฌ๋ฌ ๋ฐฉ๋ฒ์ ์๋ํ์ง๋ง ์คํจํ์ต๋๋ค. ์ด ์์ ์ ์ํํ๋ ๋ช ํํ๊ณ ๊ฐ๊ฒฐํ ๋ฐฉ๋ฒ์ด์๋ ๊ฒ ๊ฐ์ต๋๋ค.์ฌํํ๋ ค๋ฉด
์๋ :
- ๊ฐ ๊ตฌ์ฑ ์์ ๋ด์์ ์ ์ญ ์คํ์ผ์ ๊ฐ์ ธ์ต๋๋ค.
- ๊ฒฐ๊ณผ : ์คํ์ผ์ด ์ ์ฉ๋์ง๋ง ์ฌ๋ฌ global.scss ์คํ์ผ์ด {story #} x ๋ฒ ๋ฐ๋ณต๋ฉ๋๋ค.
- ์ฌ์ฉ์ ์ ์ ์นํฉ ๊ตฌ์ฑ์ ๋ํ ์ง์นจ์ ๋ฐ๋ฅด์ญ์์ค : https://storybook.js.org/docs/configurations/custom-webpack-config/
- ๊ฒฐ๊ณผ : ์ด๊ฒ์ ์คํ ๋ฆฌ ๋ถ์๊ฒ scss๋ฅผ ์ฝ๋๋ก ์ง์ ํ ๋ฟ์ด๋ฉฐ ์ค์ ๋ก ๋ชจ๋ ์คํ ๋ฆฌ์ ์ ์ฉ๋๋ ์ ์ญ ํ์ผ์ ์ค์ ํ์ง๋ ์์ต๋๋ค.
- ํ ๊ณณ์์๋ Storybook ์ฉ ๋ฐ์ฝ๋ ์ดํฐ๋ฅผ ๋ง๋ค๊ณ ๊ทธ๋ฐ ์์ผ๋ก "๊ธ๋ก๋ฒ"CSS๋ฅผ ์ ์ฉํ๋ ๊ฒ์ด ์ข์ต๋๋ค.
- ๊ฒฐ๊ณผ : ๋ด๊ฐํ๋ ค๋ ์์ (์ฌ๋ฌ ์คํ์ผ, ๋ฏน์ค ์ธ, ๋ณ์ ๋ฑ)์์ ์๋ํ์ง ์์ต๋๋ค. ์ด๊ฒ์ ์์ CSS ๋ณ๊ฒฝ์๋ง ์๋ํฉ๋๋ค.
- .storybook ์๋์ ๊ตฌ์ฑ์์ require ( '../ libs / storybook / global-styles.scss'); loadStories ํจ์ ๋ด
- ๊ฒฐ๊ณผ : ์๋ฌด๊ฒ๋
- Storybook index.ts ๋ด์์ ๊ธ๋ก๋ฒ ์คํ์ผ ๊ฐ์ ธ ์ค๊ธฐ๋ฅผ ์๋ํ์ต๋๋ค.
- ๊ฒฐ๊ณผ : ์๋ฌด๊ฒ๋
angular.json ํ์ผ์ ํตํด ์ ์ญ ์คํ์ผ ์ถ๊ฐ ์๋
- ๊ฒฐ๊ณผ : ์๋ฌด๊ฒ๋
์์๋๋ ํ๋
๋ชจ๋ ์คํ ๋ฆฌ์ Iframe์ ์ ์ฉ ํ ์ ์๋๋ก ๊ธ๋ก๋ฒ (scss) ํ์ผ์ ํ ๊ณณ์์ ๊ฐ์ ธ์ฌ ์ ์๊ธฐ๋ฅผ ๊ธฐ๋ํฉ๋๋ค. ๋ด๊ฐ ์์ง ๋ชปํ๋ ๊ตฌ์ฑ ์ค์ ์ด์์ ์ ์์ต๋๊น?์ฒด๊ณ:
- ์ด์์ฒด์ : MacOS
- ๊ธฐ๊ธฐ : Macbook Pro 2015
- ๋ธ๋ผ์ฐ์ : ํฌ๋กฌ
- ํ๋ ์ ์ํฌ : ๊ฐ๋
- ์ ๋์จ :-[addon-centered, addon-viewport, addon-info]
- ๋ฒ์ : [^ 5.0.1]
์ถ๊ฐ ์ปจํ ์คํธ
์ด ๋ฌธ์ ๋ฅผ ์ฝ๊ฒ ํด๊ฒฐํ ์์๋ ์คํ ๋ฆฌ ๋ถ์ ์ผ๋ถ ๊ตฌ์ฑ์ด ๋๋ฝ๋๊ธฐ๋ฅผ ๋ฐ๋๋๋ค.
์ด ๋ฌธ์ ๊ฐ ํด๊ฒฐ ๋ ์ ์ด ์์ต๋๊น? ๊ทธ๋ ๋ค๋ฉด ํด๊ฒฐ์ฑ ์ ๋ฌด์ ์ด์์ต๋๊น? ๊ฐ์ฌ!
@caseytrombley ํด๊ฒฐ์ฑ
์ preview.js์ import '!style-loader!css-loader!sass-loader!./styles.scss';
์ ์ถ๊ฐํ๋ ๊ฒ์
๋๋ค.
Create React App + Storybook์ ๊ฒฝ์ฐ config.js
์ ๋ค์์ ์ถ๊ฐํ์ฌ Storybook์ CSS ์ฌ์ค์ ์ ์ ์ฉํ์ต๋๋ค.
import '!style-loader!css-loader!../src/reset.css';
๋๊ตฌ๋ postcss-scss์ ๋ํ ์๋ฃจ์ ์ด ์์ต๋๊น?
์ฌ์ฉ์ ์ง์ ์นํฉ ๊ตฌ์ฑ์ ํตํด ์คํ ๋ฆฌ ๋ถ์ SCSS ์์ ๋ฅผ ๋ธ๋ก๊ทธ ๋ฅผ ์ฝ์ ์ ์์ต๋๋ค.
ํ์ฌ ๊ตฌ์ฑ ๋ฒ๊ทธ https://github.com/storybookjs/storybook/issues/11052 ๋ฐ ํด๊ฒฐ ๋ฐฉ๋ฒ์ผ๋ก ์์ฑํ ๋ธ๋ก๊ทธ๋ฅผ ์ค๋ช ํ๋ ๊ฒฐํจ์ด ์์ต๋๋ค.
๋์์ด ๋์๊ธฐ๋ฅผ ๋ฐ๋๋๋ค!
main.js
๊ฐ์๋ ์ค์น์ ๊ฒฝ์ฐ (์ : CreateReactApp ํ๋ก์ ํธ์์ npx -p @storybook/cli sb init
์คํ) preview.js
๋ฅผ ํ์ ๋ก ์ถ๊ฐ ํ ๋ค์ ๋ค์์ ์ํํฉ๋๋ค.
// .storybook/preview.js
require('!style-loader!css-loader!../src/css/tailwind-utility-classes.css')
์ด๋ฌํ ๋ก๋ ์ค ํ๋๊ฐ ์ค์น๋์ง ์์๋ค๊ณ ๋ถํํ๋ ๊ฒฝ์ฐ (CRA ํ๋ก์ ํธ ์ธ ๊ฒฝ์ฐ ํด๋น) ์๋์ผ๋ก ์ค์นํ์ญ์์ค (์ : yarn add style-loader css-loader
).
upvoted / deprecated ์๋ฃจ์ ์ด ์คํจ ํ ๋ ์ฌ๋๋ค์ด ์๋ฃจ์ ์ ์ํด ๋งจ ์๋๋ก ์ด๋ํ ์ ์๋๋ก ์ค๋ ๋๋ฅผ ์ ๊ทธ์ญ๋๊น?
@garrettmaring ๋น์ ๊ณผ ํจ๊ป ์๋ ์์๋ค
.storybook/preview.js
๋ณด๋ค๋.storybook/config.js
?
๋ด๊ฐ ๋ง๋ค ๋ styles.css
๊ฐ์ ๋๋ ํ ๋ฆฌ ๋ด์์ ํ์ผ์ํ๊ณ ๊ทธ๊ฒ์ ๊ฐ์ ธ import './styles.css';
๋ด ์์ preview.js
ํ์ผ, ๋ด ๋ํ๊ฐ ๊ฐ์๊ธฐ ๋จ์ง๋ก๋ ์ํ์ ๋ถ์ด์
๋๋ค ... :(
๊ทธ๊ฒ์ ํด๊ฒฐํ๋ ๋ฐฉ๋ฒ์ ์์ญ๋๊น?
์น ๊ตฌ์ฑ ์์์ ๋ํ open-wc ๊ตฌ์ฑ ๊ณผ ํจ๊ป ์คํ ๋ฆฌ ๋ถ์ ์ฌ์ฉํ๊ณ preview.js
๋ฐ main.js
ํ์ผ์ด .storybook
ํด๋์ ์์ต๋๋ค.
@dcts ๋ ./styles.css
๊ฐ Tailwind ์ ํธ๋ฆฌํฐ๋ฅผ ์ฐธ์กฐํ๊ฑฐ๋, ์ฌ์ ์ฒ๋ฆฌํด์ผํ๋ ์ผ๋ถ ๊ธฐ๋ฅ์ ์ฌ์ฉํ๊ฑฐ๋, ๋ค๋ฅธ ๋ฐฉ์์ผ๋ก ๋
๋ฆฝ ์คํ ํ ํ์ผ๋ก "ํ์"๋๊ธฐ ๋๋ฌธ์ผ ์ ์์ต๋๋ค.
์ธ ๊ฐ์ง๋ฅผํด์ผํ์ต๋๋ค.
css/tailwind.css
์
๋๋ค.postcss css/tailwind.css -o css/index.css
.storybook/preview.js
(ํ์ํ ๊ฒฝ์ฐ ํด๋น ํ์ผ ์์ฑ)์ import '../css/index.css
Twin ์ ์ฌ์ฉํ๊ณ ์์ผ๋ฉฐ ์์๋๋ก์ด ๊ตฌ์ฑ ์์๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค.
import tw, { styled } from 'twin.macro'
const StyledReactionButton = styled.button`
${tw`bg-blue-400 bg-opacity-25`}
`
๋ ๋์ ํด๊ฒฐ์ฑ ์ postcss ๋จ๊ณ๋ฅผ ์ฒ๋ฆฌํ๋๋ก ์คํ ๋ฆฌ ๋ถ ์นํฉ ๊ตฌ์ฑ์ ํธ์งํ๋ ๊ฒ์ ๋๋ค.
scss์ ํจ๊ป Vue.js๋ฅผ ์ฌ์ฉํ๊ณ ์์ต๋๋ค. ๋ด ๋ฌธ์ ๋ preview.js์์ ์ ์ญ scs๋ฅผ ๊ฐ์ ธ์ฌ ๋ ๋ด ํฌ๋กฌ devtolls๊ฐ ๋งค์ฐ ๋๋ฆฌ๊ณ ์ฌ์ฉํ ์ ์๋ค๋ ๊ฒ์ ๋๋ค. ๊ฐ์ ๋ฌธ์ ๊ฐ์๋ ์ฌ๋์ด ์์ต๋๊น? ๋์์ด ์์ต๋๊น? ๊ฐ ์คํ ๋ฆฌ์์ ๊ธ๋ก๋ฒ scs๋ฅผ ๊ฐ์ ธ ์ค์ง ์์ผ๋ ค ๊ณ ํฉ๋๋ค.
์์ ์๋ฃจ์ ์ ์ด๋ค ๋ฐฉ์์ผ๋ก ์๋ํ์ง๋ง __from official docs__ ์ด ๋ฐฉ๋ฒ ์ ์ ์ฒด ํ๋ก์ ํธ์์ ๋ฌธ์ ๋ฅผ ์ ๊ฑฐํฉ๋๋ค.
๊ฐ๋จํ ๋งํด, scss ๋ก๋๋ฅผ ์ค์ ํ๋ ค๋ฉด Webpack ๊ตฌ์ฑ์ ํ์ฅํด์ผํฉ๋๋ค.
// .storybook/main.js
const path = require('path');
// Export a function. Accept the base config as the only param.
module.exports = {
webpackFinal: async (config, { configType }) => {
// `configType` has a value of 'DEVELOPMENT' or 'PRODUCTION'
// You can change the configuration based on that.
// 'PRODUCTION' is used when building the static version of storybook.
// Make whatever fine-grained changes you need
config.module.rules.push({
test: /\.scss$/,
use: ['style-loader', 'css-loader', 'sass-loader'],
include: path.resolve(__dirname, '../'),
});
// Return the altered config
return config;
},
};
์ด์ .storybook/preview.js
์์ scss ํ์ผ์ ์ด์จ์ด๋ก ๊ฐ์ ธ์ฌ ์ ์์ต๋๋ค.
// .storybook/preview.js
import "../src/styles/main.scss";
// ...
... ๋ํ ๊ตฌ์ฑ ์์์์๋ :
<!-- src/components/MyComponent.vue -->
<template>
...
</template>
<script>
// ...
</script>
<style lang="scss">
<strong i="18">@import</strong> "../styles/components/components.my-component";
</style>
preview.js
์์ ์๋นํ ๊ท๋ชจ์ SCSS ํ๋ก์ ํธ๋ฅผ ๊ฐ์ ธ ์ค๋ฉด ํ๋ก์ ํธ์ ๋ณ๊ฒฝ ์ฌํญ์ ๋ํด 5 ์ด ์ด์์ ์ฌ ์ปดํ์ผ ์๊ฐ์ด ๋ฐ์ํ๋ ๋ฌธ์ ๊ฐ ๋ฐ์ํ์ต๋๋ค. ์ด๊ฒ์ ์คํ ๋ฆฌ๋ก ์ฎ๊ธฐ๋ฉด ๋ชจ๋ ๊ฒ์ด <1 ์ด์ ์ฌ ์ปดํ์ผ ์๊ฐ์ผ๋ก ๋๋์๊ฐ๋๋ค ..
Gatsby ๋ฐ ํ์ค scss ์ฌ์ ์ค์ ๊ณผ ํจ๊ป Storybook์ ์ฌ์ฉํ๊ณ global.scss
ํ์ผ์ preview.js
๊ฐ์ ธ์จ ํ ์คํ์ผ์ด ์ ํ๋์ง๋ง global.scss
๋ณ์๋ฅผ ์ ์ํ๊ณ ์ด๋ฅผ ๊ตฌ์ฑ ์์์์ ์ฌ์ฉํ๋ฉด ๋ค์ ์ค๋ฅ๊ฐ ๋ฐ์ํฉ๋๋ค.
ERROR in ./src/stories/button.scss (./node_modules/css-loader/dist/cjs.js!./node_modules/sass-loader/dist/cjs.js!./src/stories/button.scss)
Module build failed (from ./node_modules/sass-loader/dist/cjs.js):
SassError: Undefined variable: "$black".
global.scss ํ์ผ์ด ๋๋ฌด ๋ฆ๊ฒ๋ก๋๋๋ ๊ฒ ๊ฐ์ง๋ง ์ฌ๋ฐ๋ฅด๊ฒ ๊ตฌ์ฑํ๋ ๋ฐฉ๋ฒ์ ๋ชจ๋ฅด๊ฒ ์ต๋๋ค. preview.js
์ ์ญ ์คํ์ผ์ ์ถ๊ฐํ๋ ๊ฒ์ด ๊ถ์ฅ๋๋ ๋ฐฉ๋ฒ์ด๊ธฐ ๋๋ฌธ์ ๋ฒ๊ทธ ์ผ ์๋ ์์ต๋๋ค.
์ต์ ์ ๋ณด
๋ช ๋ น์ด ์๋๋๋ค. global.scss์ ๋ค์์ ์์ฑํ๋ฉด.
body {
background-color: black;
}
Button.scss์ ๋ค์์ด ์์ต๋๋ค.
body {
background-color:green;
}
๋ฐฐ๊ฒฝ์์ ์ฌ์ค ๋ น์์ ๋๋ค. ์ด๋ Buttons.scss๊ฐ Global.scss ํ์๋ก๋๋์ง๋ง ๋ณ์๋ ์ฌ์ ํ ์ ํ๋์ง ์์์ ์๋ฏธํฉ๋๋ค.
ํด๊ฒฐ์ฑ
์ด์ฆ, ์ค๋ ๋ง์ด ๋ฐฐ์ ์ด์. ์ฃผ๋ ๋ฌธ์ ๋ sass ๋ณ์๊ฐ ๊ธฐ๋ณธ์ ์ผ๋ก ์ ์ธ ๋ ํ์ผ์์๋ง ์ ์๋๋ค๋ ๊ฒ์
๋๋ค. ํ์ผ๊ฐ์ ๋ณ์๋ฅผ ์ฌ์ฉํ๋ ค๋ฉด sass-modules๋ฅผ ์ฌ์ฉํด์ผํฉ๋๋ค. ์ด์ ๋ฐฉ๋ฒ์ @import ๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด์์ง๋ง ์๋ก์ด ์ ํธ๋๋ ๋ฐฉ๋ฒ์ @use์
๋๋ค. @use pathtomodule/global.scss
button.scss
์ ๊ฐ์ ๋ชจ๋ ๋ชจ๋์ ์์ํฉ๋๋ค. ์ ๊ฒฝ์ฐ์๋ preview.js
์ ๊ฐ์ ธ ์ค๊ธฐ๊ฐ ์ค๋ณต๋ฉ๋๋ค.
๋ ๋ค๋ฅธ ๋ฌธ์ ๊ฐ์์์ต๋๋ค. @use ๋ ํ์ฌ dart sass์์๋ง ์ง์๋ฉ๋๋ค. node-sass๋ฅผ ์ค์นํ์ต๋๋ค ( npm i node-sass --save-dev
). dart-sass
( npm i dart-sass --save-dev
)๋ผ๋ ํจํค์ง๊ฐ ์์ง๋ง ์ด์ ์ ์ธ๊ธ ํ ์ฌ์ ์ค์ ์ ๋ง๋ ๊ฒ์ ์๋๋๋ค. Dart sass๋ ์ผ๋ฐ sass๊ฐ๋์์ต๋๋ค . npm i sass --save-dev
๊ฐ๋จํ ์ค์นํ ์ ์์ต๋๋ค.
์๋ ํ์ธ์ @Piepongwong ,
๋๋ Gatsby๋ฅผ ์ฌ์ฉํ์ง ์์ง๋ง Vue์์ ๋ชจ๋ mixin ๋ฐ ๋ณ์ ๊ฐ์ ธ ์ค๊ธฐ๋ฅผ _common.scss
๋ผ๋ ํ๋์ ํ์ผ์ ๋ฃ๋ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ณ ๋ค์๊ณผ ๊ฐ์ ๊ตฌ์ฑ ์์๋ก ๊ฐ์ ธ์ต๋๋ค.
<style lang="scss">
<strong i="9">@import</strong> 'path/to/styles/_common.scss';
<strong i="10">@import</strong> 'path/to/styles/components/_components.component.scss';
</style>
์ด๋ฐ ์์ผ๋ก ๋๋ ํญ์ ๋ด ๋ฏน์ค ์ธ๊ณผ ๋ณ์๊ฐ ์กด์ฌํ๋ค๊ณ ํ์ ํ์ง๋ง ๋ชจ๋ ๊ตฌ์ฑ ์์์ ๋ณ์๋ฅผ ์ฌ๋ฐ๋ฅด๊ฒ ์ถ์ํํด์ผํ๋ค๋ ์ ์ ์๊ณ ์์ต๋๋ค. ๋ชจ๋ฒ ์ฌ๋ก๋ฅผ ๋ณด๋ ค๋ฉด inuitcss ๋ฅผ ์ฐธ์กฐํ์ญ์์ค.
๋์์ด ๋์๊ธฐ๋ฅผ ๋ฐ๋๋๋ค.
์๋ ํ์ธ์ @omaracrystal ์ ์ ๊ฐ ํ ๋ฐฉ๋ฒ์ ๋๋ค.
.storybook/config.js
ํ์ผ์์ ์ฌ๋ฐ๋ฅธ ๋ก๋๋ฅผ ์ธ๋ผ์ธ์ผ๋ก ๊ฐ์ ธ ์ค๊ธฐ๋ฅผ ์ถ๊ฐํ์ญ์์ค.import '!style-loader!css-loader!sass-loader!./scss-loader.scss';
๊ทธ๋ฐ ๋ค์
scss-loader.scss
ํ์ผ์์ ํ์ํ ๋ชจ๋ ์คํ์ผ์ ์ถ๊ฐ / ๊ฐ์ ธ์ต๋๋ค.$font-path: '../projects/lib/src/theming/fonts/OpenSans/'; <strong i="15">@import</strong> '../projects/lib/src/theming/reset.scss'; <strong i="16">@import</strong> '../projects/lib/src/theming/main.scss'; html { font-family: $font-name; font-size: $font-size--small; }
@kroeder ๊ฐ ์ ์ํ ์๋ฃจ์ ์ Angular ์์ฉ ํ๋ก๊ทธ๋จ์ ๊ตฌ์ฑ ์์๋ฅผ ์ฌ์ฉํ๋ ํ ์๋ํ์ง๋ง ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๊ฒฝ์ฐ angular.json ํ์ผ์
styles
์์ฑ์ ์ถ๊ฐ ํ ์ ์์ต๋๋ค.๋ชจ๋์๊ฒ ํจ๊ณผ๊ฐ ์์ต๋๊น? ๋ด ํธ์ด ์๋๋๋ค :(
๋๋ฅผ ์ํด :( ๋๋ next.js ๋ฐ reactstrap์ผ๋ก ์๋ํ์ง๋ง ๋ด ์คํ์ผ ํด๋์์ app.scss๋ฅผ๋ก๋ํ์ง ์์์ต๋๋ค : /
์๋ ํ์ธ์ @omaracrystal ์ ์ ๊ฐ ํ ๋ฐฉ๋ฒ์ ๋๋ค.
.storybook/config.js
ํ์ผ์์ ์ฌ๋ฐ๋ฅธ ๋ก๋๋ฅผ ์ธ๋ผ์ธ์ผ๋ก ๊ฐ์ ธ ์ค๊ธฐ๋ฅผ ์ถ๊ฐํ์ญ์์ค.import '!style-loader!css-loader!sass-loader!./scss-loader.scss';
๊ทธ๋ฐ ๋ค์
scss-loader.scss
ํ์ผ์์ ํ์ํ ๋ชจ๋ ์คํ์ผ์ ์ถ๊ฐ / ๊ฐ์ ธ์ต๋๋ค.$font-path: '../projects/lib/src/theming/fonts/OpenSans/'; <strong i="16">@import</strong> '../projects/lib/src/theming/reset.scss'; <strong i="17">@import</strong> '../projects/lib/src/theming/main.scss'; html { font-family: $font-name; font-size: $font-size--small; }
@kroeder ๊ฐ ์ ์ํ ์๋ฃจ์ ์ Angular ์์ฉ ํ๋ก๊ทธ๋จ์ ๊ตฌ์ฑ ์์๋ฅผ ์ฌ์ฉํ๋ ํ ์๋ํ์ง๋ง ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๊ฒฝ์ฐ angular.json ํ์ผ์
styles
์์ฑ์ ์ถ๊ฐ ํ ์ ์์ต๋๋ค.๋ชจ๋์๊ฒ ํจ๊ณผ๊ฐ ์์ต๋๊น? ๋ด ํธ์ด ์๋๋๋ค :(
๋๋ฅผ ์ํด :( ๋๋ next.js ๋ฐ reactstrap์ผ๋ก ์๋ํ์ง๋ง ๋ด ์คํ์ผ ํด๋์์ app.scss๋ฅผ๋ก๋ํ์ง ์์์ต๋๋ค : /
@hayatbiralem ๋ฐฉ์์ผ๋ก ์๋ํ์ญ์์ค-์๋ํฉ๋๋ค.
์ด๊ฒ์ ์ต์ ๋ฒ์ ์ ๋ํ์ฑ
@storybook/[email protected]
์์ ์ ์๊ฒ ํจ๊ณผ์ ์ด์์ต๋๋ค. ๋๊ตฐ๊ฐ์๊ฒ ๋์์ด๋๊ธฐ๋ฅผ ๋ฐ๋๋๋ค!
// .storybook/main.js
const path = require('path');
module.exports = {
"stories": [
"../stories/**/*.stories.mdx",
"../stories/**/*.stories.@(js|jsx|ts|tsx)",
"../app/frontend/components/**/*.stories.@(js|jsx|ts|tsx)"
],
"addons": [
"@storybook/addon-links",
"@storybook/addon-essentials",
'@storybook/preset-scss'
],
webpackFinal: async (config) => {
// this sets the default path for modules
config.resolve.modules = [
...(config.resolve.modules || []),
path.resolve(__dirname, "../app/frontend"),
];
config.module.rules.map(rule => {
if (rule.test instanceof RegExp && rule.test.toString() === '/\\.s[ca]ss$/') {
rule.use.push({
loader: require.resolve('sass-resources-loader'),
options: {
resources: [
path.resolve(__dirname, '../app/frontend/styles/base/_variables.scss')
]
}
})
}
return rule
})
return config;
},
}
๊ฐ์ฅ ์ ์ฉํ ๋๊ธ
์๋ ํ์ธ์ @omaracrystal ์ ์ ๊ฐ ํ ๋ฐฉ๋ฒ์ ๋๋ค.
.storybook/config.js
ํ์ผ์์ ์ฌ๋ฐ๋ฅธ ๋ก๋๋ฅผ ์ธ๋ผ์ธ์ผ๋ก ๊ฐ์ ธ ์ค๊ธฐ๋ฅผ ์ถ๊ฐํ์ญ์์ค.๊ทธ๋ฐ ๋ค์
scss-loader.scss
ํ์ผ์์ ํ์ํ ๋ชจ๋ ์คํ์ผ์ ์ถ๊ฐ / ๊ฐ์ ธ์ต๋๋ค.@kroeder ๊ฐ ์ ์ํ ์๋ฃจ์ ์ Angular ์์ฉ ํ๋ก๊ทธ๋จ์ ๊ตฌ์ฑ ์์๋ฅผ ์ฌ์ฉํ๋ ํ ์๋ํ์ง๋ง ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๊ฒฝ์ฐ angular.json ํ์ผ์
styles
์์ฑ์ ์ถ๊ฐ ํ ์ ์์ต๋๋ค.