diff --git a/.eslintrc b/.eslintrc
index f1fb0cb29..ef036ad9c 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -1,6 +1,6 @@
{
"root": true,
- "extends": ["adslot", "plugin:import/typescript"],
+ "extends": ["adslot", "plugin:import/typescript", "plugin:storybook/recommended"],
"settings": {
"lodash": {
"version": 4
diff --git a/.storybook/main.ts b/.storybook/main.ts
new file mode 100644
index 000000000..e48df9108
--- /dev/null
+++ b/.storybook/main.ts
@@ -0,0 +1,29 @@
+import type { StorybookConfig } from '@storybook/react-vite';
+import { mergeConfig } from 'vite';
+import svgr from 'vite-plugin-svgr';
+
+const config: StorybookConfig = {
+ stories: ['../www/**/*.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx|mdx)'],
+ addons: ['@storybook/addon-links', '@storybook/addon-essentials', '@storybook/addon-interactions'],
+ framework: {
+ name: '@storybook/react-vite',
+ options: {},
+ },
+ docs: {
+ autodocs: 'tag',
+ },
+ staticDirs: ['../www/assets'],
+
+ async viteFinal(config) {
+ // Merge custom configuration into the default config
+ return mergeConfig(config, {
+ plugins: [
+ svgr({
+ exportAsDefault: true,
+ }),
+ ],
+ });
+ },
+};
+
+export default config;
diff --git a/.storybook/manager-head.html b/.storybook/manager-head.html
new file mode 100644
index 000000000..0d5097817
--- /dev/null
+++ b/.storybook/manager-head.html
@@ -0,0 +1,14 @@
+
+
+
+
+
+
diff --git a/.storybook/manager.js b/.storybook/manager.js
new file mode 100644
index 000000000..6d6534f28
--- /dev/null
+++ b/.storybook/manager.js
@@ -0,0 +1,6 @@
+import { addons } from '@storybook/addons';
+import theme from './theme';
+
+addons.setConfig({
+ theme,
+});
diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx
new file mode 100644
index 000000000..0eee18896
--- /dev/null
+++ b/.storybook/preview.tsx
@@ -0,0 +1,45 @@
+import React from 'react';
+import type { Preview } from '@storybook/react';
+import { Title, Subtitle, Description, Primary, ArgTypes, Stories } from '@storybook/blocks';
+
+import '../src/styles/bootstrap-custom.css';
+import '../www/storybook.css';
+
+const preview: Preview = {
+ parameters: {
+ actions: { argTypesRegex: '^on.*' },
+ controls: {
+ matchers: {
+ color: /(background|color)$/i,
+ date: /Date$/,
+ },
+ expanded: true,
+ },
+ options: {
+ storySort: {
+ order: [
+ 'Getting Started',
+ ['Introduction', 'Installation', 'Contributing'],
+ 'Design System',
+ ['Design Guide', 'Tokens', ['Overview', 'Colours', 'Typography', 'Border']],
+ 'Components',
+ 'Pending Review',
+ ],
+ },
+ },
+ docs: {
+ page: () => (
+ <>
+
+
+
+
+
+
+ >
+ ),
+ },
+ },
+};
+
+export default preview;
diff --git a/.storybook/theme.js b/.storybook/theme.js
new file mode 100644
index 000000000..5a006b1ef
--- /dev/null
+++ b/.storybook/theme.js
@@ -0,0 +1,38 @@
+import { create } from '@storybook/theming';
+
+export default create({
+ brandTitle: 'Adslot UI',
+ brandUrl: 'https://ui.adslot.com',
+ brandImage: '/aui--logo.png',
+ gridCellSize: 12,
+ base: 'light',
+
+ colorPrimary: '#006dcc',
+ colorSecondary: '#006dcc',
+
+ // UI
+ appBg: 'white',
+ appContentBg: '#ffffff',
+ appBorderColor: '#e8e8e8',
+ appBorderRadius: 3,
+
+ // Typography
+ fontBase: '"Roboto", -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif',
+ fontCode: 'monospace',
+
+ // Text colors
+ textColor: '#333',
+ textInverseColor: 'rgba(255,255,255,0.9)',
+ textMutedColor: '#838383',
+
+ // Toolbar default and active colors
+ barTextColor: '#5a5a5a',
+ barSelectedColor: '#5a5a5a',
+ barBg: '#f9f9f9',
+
+ // Form colors
+ inputBg: 'white',
+ inputBorder: '#d3d3d3',
+ inputTextColor: '#333',
+ inputBorderRadius: 3,
+});
diff --git a/config/webpack.config.dev.build.js b/config/webpack.config.dev.build.js
deleted file mode 100644
index ecd6d6cf9..000000000
--- a/config/webpack.config.dev.build.js
+++ /dev/null
@@ -1,105 +0,0 @@
-const webpack = require('webpack');
-const { merge: webpackMerge } = require('webpack-merge');
-const MiniCssExtractPlugin = require('mini-css-extract-plugin');
-const commonConfig = require('./webpack.config');
-const paths = require('./paths');
-
-// This is the development configuration.
-// It is focused on developer experience and fast rebuilds.
-// The production configuration is different and lives in a separate file.
-module.exports = webpackMerge(commonConfig, {
- mode: 'development',
- // You may want 'eval' instead if you prefer to see the compiled output in DevTools.
- // See the discussion in https://github.com/facebookincubator/create-react-app/issues/343.
- devtool: 'eval-source-map',
- entry: paths.appSrc,
- output: {
- path: paths.appDist,
- filename: 'adslot-ui.js',
- libraryTarget: 'umd',
- library: 'AdslotUI',
- },
- externals: {
- lodash: {
- root: '_',
- commonjs2: 'lodash',
- commonjs: 'lodash',
- amd: 'lodash',
- },
- react: {
- root: 'React',
- commonjs2: 'react',
- commonjs: 'react',
- amd: 'react',
- },
- 'react-dom': {
- root: 'ReactDOM',
- commonjs2: 'react-dom',
- commonjs: 'react-dom',
- amd: 'react-dom',
- },
- moment: {
- root: 'moment',
- commonjs2: 'moment',
- commonjs: 'moment',
- amd: 'moment',
- },
- },
- module: {
- strictExportPresence: true,
- rules: [
- {
- test: /\.(eot|ttf|woff|woff2)$/,
- type: 'asset',
- parser: {
- dataUrlCondition: { maxSize: 8 * 1024 },
- },
- },
- {
- test: /\.(js|jsx)$/,
- include: paths.appSrc,
- loader: 'babel-loader',
- options: {
- cacheDirectory: true,
- },
- },
- {
- test: /\.css$/i,
- use: [
- MiniCssExtractPlugin.loader,
- {
- loader: 'css-loader',
- options: {
- importLoaders: 1,
- sourceMap: false,
- },
- },
- {
- loader: 'postcss-loader',
- },
- ],
- },
- {
- test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/, /\.svg$/],
- type: 'asset',
- parser: {
- dataUrlCondition: { maxSize: 10000 },
- },
- generator: {
- filename: 'static/media/[name].[hash:8][ext]',
- },
- },
- ],
- },
- plugins: [
- new MiniCssExtractPlugin({
- filename: 'adslot-ui.css',
- }),
- // Moment.js is an extremely popular library that bundles large locale files
- // by default due to how Webpack interprets its code. This is a practical
- // solution that requires the user to opt into importing specific locales.
- // https://github.com/jmblog/how-to-optimize-momentjs-with-webpack
- // You can remove this if you don't use Moment.js:
- new webpack.IgnorePlugin({ resourceRegExp: /^\.\/locale$/, contextRegExp: /moment$/ }),
- ],
-});
diff --git a/config/webpack.config.dev.js b/config/webpack.config.dev.js
deleted file mode 100644
index 777b65c6b..000000000
--- a/config/webpack.config.dev.js
+++ /dev/null
@@ -1,123 +0,0 @@
-const path = require('path');
-const emoji = require('remark-emoji');
-const webpack = require('webpack');
-const { merge: webpackMerge } = require('webpack-merge');
-const HtmlWebpackPlugin = require('html-webpack-plugin');
-const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
-const commonConfig = require('./webpack.config');
-const paths = require('./paths');
-
-// This is the development configuration.
-// It is focused on developer experience and fast rebuilds.
-// The production configuration is different and lives in a separate file.
-module.exports = webpackMerge(commonConfig, {
- mode: 'development',
- // You may want 'eval' instead if you prefer to see the compiled output in DevTools.
- // See the discussion in https://github.com/facebookincubator/create-react-app/issues/343.
- devtool: 'cheap-module-source-map',
- // These are the "entry points" to our application.
- // This means they will be the "root" imports that are included in JS bundle.
- // The first two entry points enable "hot" CSS and auto-refreshes for JS.
- entry: ['webpack-dev-server/client?/', 'webpack/hot/dev-server', paths.appIndexJs],
- output: {
- // Next line is not used in dev but WebpackDevServer crashes without it:
- path: console.log(paths.appBuild) || paths.appBuild,
- // Add /* filename */ comments to generated require()s in the output.
- pathinfo: true,
- // This does not produce a real file. It's just the virtual path that is
- // served by WebpackDevServer in development. This is the JS bundle
- // containing code from all our entry points, and the Webpack runtime.
- filename: 'static/js/[id].bundle.js',
- // There are also additional JS chunk files if you use code splitting.
- chunkFilename: 'static/js/[name].chunk.js',
- // This is the URL that app is served from. We use "/" in development.
- publicPath: '/',
- // Point sourcemap entries to original disk location (format as URL on Windows)
- devtoolModuleFilenameTemplate: (info) => path.resolve(info.absoluteResourcePath).replace(/\\/g, '/'),
- },
- cache: { type: 'filesystem' },
- module: {
- rules: [
- {
- test: /\.(md|mdx)?$/,
- include: [paths.appSrc, paths.appDemo],
- use: [
- { loader: 'babel-loader' },
- {
- loader: '@mdx-js/loader',
- options: {
- remarkPlugins: [emoji],
- },
- },
- ],
- },
- {
- test: /\.(eot|ttf|woff|woff2)$/,
- type: 'asset',
- parser: {
- dataUrlCondition: { maxSize: 8 * 1024 },
- },
- },
- {
- test: /\.(js|jsx)$/,
- include: [paths.appSrc, paths.appDemo],
- loader: 'babel-loader',
- options: {
- cacheDirectory: true,
- },
- },
- {
- test: /\.css$/i,
- sideEffects: true,
- use: [
- 'style-loader',
- {
- loader: 'css-loader',
- options: {
- importLoaders: 1,
- },
- },
- {
- loader: 'postcss-loader',
- },
- ],
- },
- {
- test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/, /\.svg$/],
- type: 'asset',
- generator: {
- filename: 'static/media/[name].[hash:8][ext]',
- },
- },
- ],
- },
- optimization: {
- splitChunks: {
- chunks: 'all',
- name: 'vendors',
- },
- runtimeChunk: 'single',
- },
- plugins: [
- new HtmlWebpackPlugin({
- inject: true,
- template: paths.appHtml,
- }),
- new webpack.DefinePlugin({
- 'process.env.NODE_ENV': '"development"',
- }),
- // This is necessary to emit hot updates (currently CSS only):
- new webpack.HotModuleReplacementPlugin(),
- // Watcher doesn't work well if you mistype casing in a path so we use
- // a plugin that prints an error when you attempt to do this.
- // See https://github.com/facebookincubator/create-react-app/issues/240
- new CaseSensitivePathsPlugin(),
- // Moment.js is an extremely popular library that bundles large locale files
- // by default due to how Webpack interprets its code. This is a practical
- // solution that requires the user to opt into importing specific locales.
- // https://github.com/jmblog/how-to-optimize-momentjs-with-webpack
- // You can remove this if you don't use Moment.js:
- new webpack.IgnorePlugin({ resourceRegExp: /^\.\/locale$/, contextRegExp: /moment$/ }),
- new webpack.ProgressPlugin(),
- ],
-});
diff --git a/config/webpack.config.dist.js b/config/webpack.config.dist.js
index cf42b1cec..52abe7fdc 100644
--- a/config/webpack.config.dist.js
+++ b/config/webpack.config.dist.js
@@ -1,4 +1,3 @@
-const emoji = require('remark-emoji');
const webpack = require('webpack');
const { merge: webpackMerge } = require('webpack-merge');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
@@ -54,19 +53,6 @@ module.exports = webpackMerge(commonConfig, {
},
module: {
rules: [
- {
- test: /\.(md|mdx)?$/,
- include: [paths.appSrc, paths.appDemo],
- use: [
- { loader: 'babel-loader' },
- {
- loader: '@mdx-js/loader',
- options: {
- remarkPlugins: [emoji],
- },
- },
- ],
- },
{
test: /\.(eot|ttf|woff|woff2)$/,
type: 'asset',
@@ -76,7 +62,7 @@ module.exports = webpackMerge(commonConfig, {
},
{
test: /\.(js|jsx)$/,
- include: [paths.appSrc, paths.appDemo],
+ include: paths.appSrc,
loader: 'babel-loader',
options: {
cacheDirectory: true,
@@ -129,7 +115,7 @@ module.exports = webpackMerge(commonConfig, {
},
}),
new CssMinimizerPlugin({
- minify: (data, inputMap, minimizerOptions) => {
+ minify: (data) => {
const cssnano = require('cssnano');
const safe = require('postcss-safe-parser');
diff --git a/config/webpack.config.prod.js b/config/webpack.config.prod.js
deleted file mode 100644
index 8002d33a6..000000000
--- a/config/webpack.config.prod.js
+++ /dev/null
@@ -1,196 +0,0 @@
-const webpack = require('webpack');
-const emoji = require('remark-emoji');
-const { merge: webpackMerge } = require('webpack-merge');
-const MiniCssExtractPlugin = require('mini-css-extract-plugin');
-const HtmlWebpackPlugin = require('html-webpack-plugin');
-const TerserPlugin = require('terser-webpack-plugin');
-const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
-const commonConfig = require('./webpack.config');
-const paths = require('./paths');
-
-// Assert this just to be safe.
-if (process.env.NODE_ENV !== 'production') {
- throw new Error('Production builds must have NODE_ENV=dist.');
-}
-
-// This dist is used for creating the minified .css file.
-// The output .js file will be removed.
-module.exports = webpackMerge(commonConfig, {
- mode: 'production',
- // Don't attempt to continue if there are any errors.
- bail: true,
- devtool: false,
- entry: paths.appDemo,
- output: {
- // The build folder.
- path: paths.appBuild,
- // Generated JS file names (with nested folders).
- // There will be one main bundle, and one file per asynchronous chunk.
- // We don't currently advertise code splitting but Webpack supports it.
- filename: 'static/adslot-ui-docs.prod.js',
- publicPath: '/',
- libraryTarget: 'umd',
- library: 'AdslotUI',
- },
- externals: {
- lodash: {
- root: '_',
- commonjs2: 'lodash',
- commonjs: 'lodash',
- amd: 'lodash',
- },
- react: {
- root: 'React',
- commonjs2: 'react',
- commonjs: 'react',
- amd: 'react',
- },
- 'react-dom': {
- root: 'ReactDOM',
- commonjs2: 'react-dom',
- commonjs: 'react-dom',
- amd: 'react-dom',
- },
- moment: {
- root: 'moment',
- commonjs2: 'moment',
- commonjs: 'moment',
- amd: 'moment',
- },
- },
- module: {
- rules: [
- {
- test: /\.(md|mdx)?$/,
- include: [paths.appSrc, paths.appDemo],
- use: [
- { loader: 'babel-loader' },
- {
- loader: '@mdx-js/loader',
- options: {
- remarkPlugins: [emoji],
- },
- },
- ],
- },
- {
- test: /\.(eot|ttf|woff|woff2)$/,
- type: 'asset',
- parser: {
- dataUrlCondition: { maxSize: 8 * 1024 },
- },
- },
- {
- test: /\.(js|jsx)$/,
- include: [paths.appSrc, paths.appDemo],
- loader: 'babel-loader',
- },
- {
- test: /\.css$/i,
- use: [
- MiniCssExtractPlugin.loader,
- {
- loader: 'css-loader',
- options: {
- importLoaders: 1,
- sourceMap: false,
- },
- },
- {
- loader: 'postcss-loader',
- },
- ],
- },
- {
- test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/, /\.svg$/],
- type: 'asset',
- generator: {
- filename: 'static/media/[name].[hash:8][ext]',
- },
- },
- ],
- },
- optimization: {
- minimizer: [
- new TerserPlugin({
- parallel: true,
- extractComments: false,
- terserOptions: {
- ecma: 8,
- compress: {
- warnings: false,
- comparisons: false,
- inline: 2,
- drop_console: true,
- },
- output: {
- comments: false,
- },
- },
- }),
- new CssMinimizerPlugin({
- minify: (data, inputMap, minimizerOptions) => {
- const cssnano = require('cssnano');
- const safe = require('postcss-safe-parser');
-
- const [[filename, input]] = Object.entries(data);
-
- const postcssOptions = {
- from: filename,
- to: filename,
- map: false,
- parser: safe,
- };
-
- return cssnano({
- preset: [
- 'default',
- {
- convertValues: false,
- },
- ],
- })
- .process(input, postcssOptions)
- .then((result) => {
- return {
- code: result.css,
- map: result.map,
- warnings: result.warnings(),
- };
- });
- },
- }),
- ],
- },
- plugins: [
- // Generates an `index.html` file with the
-
-
-
-
-
-
-
-
-
-
-