Skip to content

Commit

Permalink
fixed storybook integration
Browse files Browse the repository at this point in the history
  • Loading branch information
giniedp committed Jul 7, 2023
1 parent 939a5c2 commit e79d5e2
Show file tree
Hide file tree
Showing 20 changed files with 3,043 additions and 414 deletions.
2 changes: 1 addition & 1 deletion angular.webpack.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const EmitFilePlugin = require('emit-file-webpack-plugin');
* Custom angular webpack configuration
*/
module.exports = (config, options) => {
if (options.fileReplacements) {
if (options && options.fileReplacements) {
for (let replacement of options.fileReplacements) {
let parts = replacement.with.split('.')
if (parts.includes('electron')) {
Expand Down
Empty file added apps/storybook/assets/.gitkeep
Empty file.
31 changes: 31 additions & 0 deletions apps/storybook/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import type { StorybookConfig } from '@storybook/angular'

const config: StorybookConfig = {
stories: ['../web/**/*.stories.[tj]s'],
staticDirs: [
'./assets',
{
from: '../web/assets',
to: '/assets',
},
],
framework: {
name: '@storybook/angular',
options: {},
},
addons: [
'@storybook/addon-viewport',
'@storybook/addon-controls',
'@storybook/addon-outline',
'@storybook/addon-a11y',
'@storybook/addon-interactions',
'@storybook/addon-coverage',
'@storybook/addon-docs',
'storybook-addon-mock',
],
docs: {
autodocs: 'tag',
},
}

export default config
File renamed without changes.
22 changes: 0 additions & 22 deletions apps/storybook/preview.js

This file was deleted.

25 changes: 25 additions & 0 deletions apps/storybook/preview.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Preview } from '@storybook/angular'

const preview: Preview = {
parameters: {
backgrounds: {
default: 'dark',
values: [
{
name: 'dark',
value: '#000000',
},
{
name: 'light',
value: '#FFFFFF',
},
{
name: 'transparent',
value: 'rgba(0, 0, 0, 0)',
},
],
},
},
}

export default preview
5 changes: 4 additions & 1 deletion apps/storybook/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
{
"compilerOptions": {
"skipLibCheck": true
},
"extends": "../../tsconfig.json",
"exclude": ["../web/test.ts", "../web/**/*.spec.ts"],
"include": ["../web/**/*"]
"include": ["../web/**/*", "./*"]
}
39 changes: 39 additions & 0 deletions apps/storybook/webpack.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
const angularWebpack = require('../../angular.webpack')
const path = require('path')

class IgnoreNotFoundExportPlugin {
apply(compiler) {
const messageRegExp = /export '.*'( \(imported as '.*'\))? was not found in/
compiler.hooks.done.tap('IgnoreNotFoundExportPlugin', (stats) => {
stats.compilation.warnings = stats.compilation.warnings.filter((warn) => {
return warn.constructor.name === 'ModuleDependencyWarning' && !messageRegExp.test(warn.message)
})
})
}
}

module.exports = async ({ config, mode }) => {
config.plugins.push(new IgnoreNotFoundExportPlugin())
patchSvgLoader(config)
coverageInstrument(config)
return angularWebpack(config)
}

function patchSvgLoader(config) {
const rule = config.module.rules.find(({ test, type }) => type === 'asset/resource' && test.test('.svg'))
rule.test = /\.(ico|jpg|jpeg|png|apng|gif|eot|otf|webp|ttf|woff|woff2|cur|ani|pdf)(\?.*)?$/
}

function coverageInstrument(config) {
const rules = config.module?.rules || []
rules.push({
test: /\.(js|ts)$/,
loader: '@jsdevtools/coverage-istanbul-loader',
enforce: 'post',
include: path.join(process.cwd(), 'apps', 'web', 'src'),
exclude: [/\.(e2e|spec|stories)\.ts$/, /node_modules/, /(ngfactory|ngstyle)\.js/, /polyfills.ts/],
})

config.module = config.module || {}
config.module.rules = rules
}
166 changes: 166 additions & 0 deletions apps/web/app/test/story-controls.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
import { ArgTypes } from '@storybook/angular'
import type { ArgType } from '@storybook/api'

export interface CommonArgType<T> extends ArgType {
defaultValue?: T
value?: T
}

export interface NumberArgType<T> extends ArgType {
defaultValue?: T
min?: number
max?: number
step?: number
}

export interface EnumArgType<T> extends ArgType {
defaultValue?: T
options: Array<T> | Array<{ label: string; value: T }> | Record<string, T>
}

function convertOptions<T>(
options: Array<T> | Array<{ label: string; value: T }> | Record<string, T>
): Array<{ label: string; value: T }> {
if (!options) {
return []
}
if (!Array.isArray(options)) {
return Object.entries(options).map(([label, value]) => ({ label, value }))
}
return options.map((it: any) => {
if (typeof it === 'string' || typeof it === 'number' || typeof it === 'boolean') {
return {
label: String(it),
value: it,
}
}
return it
})
}

function buildSelectType(key: string, type: string, options: EnumArgType<any>) {
const opts = convertOptions(options.options)
const mapping = opts.reduce((res, it) => {
res[it.label] = it.value
return res
}, {})
const labels = opts.map((it) => it.label)
return {
control: {
type: type,
},
name: key,
defaultValue: labels[0],
...options,
options: labels,
mapping: mapping,
}
}

export class StoryControlsBuilder<TArgs> {
public static build<TArgs>(fn: (builder: StoryControlsBuilder<TArgs>) => unknown) {
const b = new StoryControlsBuilder<TArgs>()
fn(b)
return b.build()
}

private types: Partial<ArgTypes<TArgs>> = {}

public add(key: keyof TArgs, arg: ArgType) {
this.types[key] = arg
return this
}

public boolean<K extends keyof TArgs>(key: keyof TArgs, options: CommonArgType<TArgs[K]> = {}) {
return this.add(key, {
control: { type: 'boolean' },
name: String(key),
defaultValue: false,
...options,
})
}
public text<K extends keyof TArgs>(key: keyof TArgs, options: CommonArgType<TArgs[K]> = {}) {
return this.add(key, {
control: { type: 'text' },
name: String(key),
defaultValue: '',
...options,
})
}
public number<K extends keyof TArgs>(key: keyof TArgs, options: NumberArgType<TArgs[K]> = {}) {
return this.add(key, {
control: { type: 'number' },
name: String(key),
defaultValue: null,
...options,
})
}
public range<K extends keyof TArgs>(key: keyof TArgs, options: NumberArgType<TArgs[K]> = {}) {
return this.add(key, {
control: { type: 'range' },
name: String(key),
defaultValue: null,
...options,
})
}
public date<K extends keyof TArgs>(key: keyof TArgs, options: CommonArgType<TArgs[K]> = {}) {
return this.add(key, {
control: { type: 'date' },
name: String(key),
defaultValue: null,
...options,
})
}
public color<K extends keyof TArgs>(key: keyof TArgs, presetColors: string[]) {
return this.add(key, {
control: { type: 'color', presetColors },
name: String(key),
defaultValue: null,
})
}
public object<K extends keyof TArgs>(key: keyof TArgs, options: CommonArgType<TArgs[K]> = {}) {
return this.add(key, {
control: { type: 'object' },
name: String(key),
defaultValue: {},
...options,
})
}
public array<K extends keyof TArgs>(key: keyof TArgs, options: CommonArgType<TArgs[K]> = {}) {
return this.add(key, {
control: { type: 'object' },
name: String(key),
defaultValue: [],
...options,
})
}
public file(key: keyof TArgs, options: { accept?: string } = {}) {
return this.add(key, {
control: { type: 'file' },
...options,
})
}
public radio<K extends keyof TArgs>(key: K, options: EnumArgType<TArgs[K]>) {
return this.add(key, buildSelectType(key as string, 'radio', options))
}
public radioInline = <K extends keyof TArgs>(key: K, options: EnumArgType<TArgs[K]>) => {
return this.add(key, buildSelectType(key as string, 'inline-radio', options))
}
public check = <K extends keyof TArgs>(key: K, options: EnumArgType<TArgs[K]>) => {
return this.add(key, buildSelectType(key as string, 'check', options))
}
public checkInline = <K extends keyof TArgs>(key: K, options: EnumArgType<TArgs[K]>) => {
return this.add(key, buildSelectType(key as string, 'inline-check', options))
}
public select = <K extends keyof TArgs>(key: K, options: EnumArgType<TArgs[K]>) => {
return this.add(key, buildSelectType(key as string, 'select', options))
}
public selectMulti = <K extends keyof TArgs>(key: K, options: EnumArgType<TArgs[K]>) => {
return this.add(key, buildSelectType(key as string, 'multi-select', options))
}
public append = (fn: (b: StoryControlsBuilder<TArgs>) => void) => {
fn(this)
}
public as = <R>() => this as any as StoryControlsBuilder<R>
public build = () => this.types
}
Loading

0 comments on commit e79d5e2

Please sign in to comment.