Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions .storybook/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/** @type { import('@storybook/react-webpack5').StorybookConfig } */
const config = {
stories: ["../storybook/**/*.stories.@(js|jsx)"],
addons: ["@storybook/addon-essentials"],
framework: {
name: "@storybook/react-webpack5",
options: {},
},
webpackFinal: (config) => {
config.module.rules = config.module.rules.concat([
{
test: /\.html$/,
type: "asset/source",
},
]);
return config;
},
};

module.exports = config;
26 changes: 26 additions & 0 deletions .storybook/middleware.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
const { createProxyMiddleware } = require("http-proxy-middleware");
const {
createDjangoAPIMiddleware,
} = require("storybook-django/src/middleware");

const djangoOrigin = process.env.DJANGO_ORIGIN || "http://localhost:8000";

// storybook-django middleware for the pattern-library API (handles POST body restreaming)
const djangoAPI = createDjangoAPIMiddleware({
origin: djangoOrigin,
apiPath: ["/pattern-library/"],
});

module.exports = function expressMiddleware(router) {
// Pattern library API proxy (POST requests with JSON body)
djangoAPI(router);

// Static files proxy (CSS, JS, images, fonts)
router.use(
"/static/",
createProxyMiddleware({
target: djangoOrigin,
changeOrigin: true,
})
);
};
83 changes: 83 additions & 0 deletions .storybook/preview-head.html
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at the explicit imports on this file, I'm concerned that this leaves room for unexpected drifts between base.html and this file when we load new files.

One idea to consider – perhaps we can create a small .html that takes care of loading these scripts/stylesheet, then both base.html and this file can share it. It definitely calls for a refactor on base.html though, I'm not sure if we really want to touch it 😅. What do you think of this?

Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<!-- Fonts and icons (external CDNs — loaded directly) -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
Comment on lines +2 to +3
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these 2 can be removed since we are not using Google Font

<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@24,400,0,0">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.6.0/css/all.min.css">
Comment on lines +4 to +5
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems to me like Font Awesome is only being used in _social_icon_links.html, which are used in _footer_v3.html – I have a feeling this icon library can be removed, we should look into this to confirm and remove them in a separate PR :)


<!-- Project CSS (proxied to Django via middleware) -->
<link href="/static/css/styles.css" rel="stylesheet">
<link href="/static/css/components.css" rel="stylesheet">
<link href="/static/css/boostlook.css" rel="stylesheet">
<link href="/static/css/v3/components.css" rel="stylesheet">

<!-- Alpine.js -->
<script src="https://cdn.jsdelivr.net/npm/@ryangjchandler/alpine-clipboard@2.x.x/dist/alpine-clipboard.js" defer></script>
<script src="https://cdn.jsdelivr.net/npm/@alpinejs/persist@3.x.x/dist/cdn.min.js" defer></script>
<script src="//unpkg.com/alpinejs" defer></script>

<!-- Project JS — scripts that only need to load once (utilities, global listeners) -->
<script src="/static/js/theme_handling.js"></script>
<script src="/static/js/utils.js"></script>
<script src="/static/js/highlight.js"></script>
<script src="/static/js/highlight-block.js"></script>
<script src="/static/js/dialog.js" defer></script>
<script src="/static/js/install-card.js" defer></script>

<!-- Theme meta tags (same as base.html) -->
<meta class="meta-theme" name="theme-color" content="#FAFAFA" data-dark="#18181B" data-light="#FAFAFA">
<meta class="meta-theme" name="color-scheme" content="light" data-dark="dark" data-light="light">

<!-- Hide carousel scrollbar (the track scrolls via JS, scrollbar is unwanted) -->
<style>
.post-cards--horizontal .post-cards__list {
-ms-overflow-style: none;
scrollbar-width: none;
}
.post-cards--horizontal .post-cards__list::-webkit-scrollbar {
display: none;
}
</style>

<!--
Prevent demo links (href="#", href="#_", href="#someId") from navigating
the Storybook iframe. In a real page these are harmless hash changes,
but Storybook intercepts them as route changes.
-->
<script>
document.addEventListener('click', function(e) {
var link = e.target.closest('a[href]');
if (!link) return;
var href = link.getAttribute('href');
if (href && (href === '#' || href.charAt(0) === '#')) {
e.preventDefault();
if (href !== '#') {
window.location.hash = href.substring(1);
}
}
}, true);
</script>

<!--
Scripts that self-initialize via readyState check (carousel.js, code-block.js)
must be re-loaded AFTER storybook-django injects Pattern HTML.
storybook-django fires a synthetic DOMContentLoaded after each Pattern render,
so we dynamically load these scripts on each DOMContentLoaded event.
-->
<script>
(function() {
var scriptsToReinit = [
'/static/js/carousel.js',
'/static/js/code-block.js'
];
document.addEventListener('DOMContentLoaded', function() {
scriptsToReinit.forEach(function(src) {
var old = document.querySelector('script[data-storybook-reinit="' + src + '"]');
if (old) old.parentNode.removeChild(old);
var s = document.createElement('script');
s.src = src + '?_=' + Date.now();
s.setAttribute('data-storybook-reinit', src);
document.body.appendChild(s);
});
});
})();
</script>
54 changes: 54 additions & 0 deletions .storybook/preview.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/** @type { import('@storybook/react').Preview } */
const preview = {
parameters: {
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/i,
},
},
backgrounds: {
default: 'light',
values: [
{
name: 'light',
value: '#FAFAFA',
},
{
name: 'dark',
value: '#18181B',
},
],
},
},
decorators: [
(Story, context) => {
// Add v3 class to both html and body for v3 component styles
document.documentElement.classList.add("v3");
document.body.classList.add("v3");

// Sync Storybook's background selector with project's theme system
const bgValue = context.globals.backgrounds?.value;
if (bgValue) {
const bgConfig = context.parameters.backgrounds?.values?.find((b) => b.value === bgValue);
const theme = bgConfig?.name === 'dark' ? 'dark' : 'light';
// Use the project's saveColorMode function if available
if (typeof window.saveColorMode === 'function') {
window.saveColorMode(theme);
} else {
// Fallback: directly set localStorage and dispatch event
localStorage.setItem('colorMode', theme);
window.dispatchEvent(new StorageEvent('storage', {
key: 'colorMode',
oldValue: localStorage.getItem('colorMode'),
newValue: theme,
}));
}
}

return Story();
},
],
};

export default preview;
24 changes: 24 additions & 0 deletions config/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@
host_list = env.list("ALLOWED_HOSTS", default="localhost")
ALLOWED_HOSTS = [el.strip() for el in host_list]

# Add 'web' for Docker container-to-container communication (Storybook -> Django)
if "web" not in ALLOWED_HOSTS:
ALLOWED_HOSTS.append("web")


INSTALLED_APPS = [
"django_admin_env_notice", # Third-party
Expand Down Expand Up @@ -109,6 +113,14 @@
"taggit",
]

# Pattern Library (for Storybook) — optional, only when installed
try:
import pattern_library # noqa: F401

INSTALLED_APPS += ["pattern_library"]
except ImportError:
pass

# Our Apps
INSTALLED_APPS += [
"ak",
Expand All @@ -126,6 +138,18 @@
"asciidoctor_sandbox",
]

# django-pattern-library settings (used by storybook-django)
PATTERN_LIBRARY = {
"SECTIONS": (
("v3/includes", ["v3/includes"]),
("v3/examples", ["v3/examples"]),
("includes", ["includes"]),
),
"TEMPLATE_SUFFIX": ".html",
"PATTERN_BASE_TEMPLATE_NAME": "patterns/base.html",
"BASE_TEMPLATE_NAMES": ["patterns/base.html"],
}

AUTH_USER_MODEL = "users.User"
CSRF_COOKIE_HTTPONLY = True
# See https://docs.djangoproject.com/en/4.2/ref/settings/#csrf-trusted-origins
Expand Down
8 changes: 8 additions & 0 deletions config/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -509,4 +509,12 @@
]
)

# Pattern library (for Storybook) — only when django-pattern-library is installed
try:
from pattern_library import urls as pattern_library_urls

urlpatterns.insert(0, path("pattern-library/", include(pattern_library_urls)))
except ImportError:
pass

handler404 = "ak.views.custom_404_view"
19 changes: 19 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,25 @@ services:
- .:/code
stop_signal: SIGKILL

storybook:
build:
context: .
dockerfile: docker/Dockerfile.storybook
depends_on:
- web
environment:
- "DJANGO_ORIGIN=http://web:8000"
networks:
- backend
- frontend
ports:
- "6006:6006"
volumes:
- ./.storybook:/code/.storybook
- ./storybook:/code/storybook
- ./templates:/code/templates
- ./static:/code/static

maildev:
image: maildev/maildev
init: true
Expand Down
15 changes: 15 additions & 0 deletions docker/Dockerfile.storybook
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
FROM node:22-slim

WORKDIR /code

COPY package.json yarn.lock ./
RUN yarn install

COPY .storybook/ .storybook/
COPY storybook/ storybook/
COPY templates/ templates/
COPY static/ static/

EXPOSE 6006

CMD ["npx", "storybook", "dev", "-p", "6006", "--host", "0.0.0.0"]
Loading
Loading