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
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Force include
!.*.js
!.buildkite
!.storybook
!packages/charts/.*.js

# Ignore
Expand Down
30 changes: 30 additions & 0 deletions .storybook/backgrounds.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import euiBorealisDarkVars from '@elastic/eui-theme-borealis/lib/eui_theme_borealis_dark.json';
import euiBorealisLightVars from '@elastic/eui-theme-borealis/lib/eui_theme_borealis_light.json';

interface BackgroundsOption {
name: string;
value: string;
}
type BackgroundsOptions = Record<string, BackgroundsOption>

export const backgroundsOptions = {
emptyShadeDark: { name: 'Empty Shade - Dark', value: euiBorealisDarkVars.euiColorEmptyShade },
emptyShadeLight: { name: 'Empty Shade - Light', value: euiBorealisLightVars.euiColorEmptyShade },
black: { name: 'Black', value: '#000' },
white: { name: 'White', value: '#fff' },
red: { name: 'Red', value: '#f04d9a' },
blue: { name: 'Blue', value: '#14abf5' },
yellow: { name: 'Yellow', value: '#fec709' },
green: { name: 'Green', value: '#00c1b4' },
gray: { name: 'Gray', value: 'rgb(237, 240, 245)' },
} satisfies BackgroundsOptions

export type BackgroundKey = keyof (typeof backgroundsOptions);
128 changes: 128 additions & 0 deletions .storybook/decorator.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import { EuiProvider, EuiMarkdownFormat, EuiFlexGroup, EuiFlexItem, EuiHorizontalRule, EuiText } from '@elastic/eui';
import classNames from 'classnames';
import type { CSSProperties, FC, PropsWithChildren } from 'react';
import React, { useEffect } from 'react';
import type { DecoratorFunction } from 'storybook/internal/types';
import events from 'storybook/internal/core-events';

import type { StoryGlobals, StoryParameters } from './types';
import { ThemeId, ThemeIdProvider, BackgroundIdProvider } from '@ech/sb';

import './load_icons';
import { getThemeFromTitle } from './themes';
import type { BackgroundKey } from './backgrounds';
import { addons } from 'storybook/internal/preview-api';

const ResizeWrapper: FC<PropsWithChildren<{ resize: StoryParameters['resize'] }>> = ({ resize, children }) =>
resize ? (
<div id="story-resize-wrapper" style={resize === true ? {} : resize}>
{children}
</div>
) : (
<>{children}</>
);

export const StoryDecorator: DecoratorFunction = (Story, context) => {
if (!Story) return <div>No Story</div>;

const globals = (context.globals as StoryGlobals) ?? {};

const parameters = (context.parameters as StoryParameters) ?? {};

const themeId = getThemeFromTitle(globals.theme) ?? ThemeId.Light;
const backgroundId = globals.backgrounds.value as BackgroundKey;
const {
showHeader = false,
showChartTitle = false,
showChartDescription = false,
showChartBoundary = false,
} = globals.toggles ?? {};
const { markdown, resize } = parameters;
const colorMode = themeId.includes('light') ? 'light' : 'dark';

const channel = addons.getChannel();

// useEffect(() => {
// const event = events.ARGTYPES_INFO_REQUEST
// channel.on(event, (payload) => {
// console.log(event, payload);
// });
// }, [])
useEffect(() => {
const eventss = [
events.ARGTYPES_INFO_REQUEST,
events.ARGTYPES_INFO_RESPONSE,
events.STORY_ARGS_UPDATED,
events.RESET_STORY_ARGS,
events.UPDATE_STORY_ARGS,
]
Object.values(eventss).forEach((event) => {
channel.on(event, (payload) => {
console.log(event, payload);
});
});
}, [])

return (
<EuiProvider colorMode={colorMode}>
<ThemeIdProvider value={themeId}>
<BackgroundIdProvider value={backgroundId}>
<EuiFlexGroup gutterSize="none" direction="column" responsive={false}>
{showHeader && !showChartTitle && !showChartDescription && (
<EuiFlexItem className="story-header" grow={false}>
<EuiFlexGroup gutterSize="none" direction="column" responsive={false}>
<EuiFlexItem>
<EuiText>
<h1 style={{ fontWeight: 200 }}>{context.title}</h1>
</EuiText>
</EuiFlexItem>

<EuiFlexItem>
<EuiText>
<h2 style={{ fontWeight: 400 }}>{context.name}</h2>
</EuiText>
</EuiFlexItem>

<EuiHorizontalRule />
</EuiFlexGroup>
</EuiFlexItem>
)}

<EuiFlexItem grow={false}>
<div
id="story-root"
className={classNames({
showChartBoundary,
resizeHeight: (resize as CSSProperties)?.height === undefined,
})}
>
<ResizeWrapper resize={resize}>
<Story
{...context}
chartProps={{
title: showChartTitle ? context.title : undefined,
description: showChartDescription ? context.name : undefined,
}}
/>
</ResizeWrapper>
</div>
</EuiFlexItem>
{markdown && (
<EuiFlexItem className="markdown">
<EuiMarkdownFormat>{markdown}</EuiMarkdownFormat>
</EuiFlexItem>
)}
</EuiFlexGroup>
</BackgroundIdProvider>
</ThemeIdProvider>
</EuiProvider>
);
};
37 changes: 37 additions & 0 deletions .storybook/load_icons.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

// @ts-nocheck - EUI icon assets have no types
// see https://github.com/elastic/eui/issues/5463

import { icon as visualizeAppIcon } from '@elastic/eui/es/components/icon/assets/app_visualize';
import { icon as arrowDownIcon } from '@elastic/eui/es/components/icon/assets/arrow_down';
import { icon as arrowLeftIcon } from '@elastic/eui/es/components/icon/assets/arrow_left';
import { icon as arrowRightIcon } from '@elastic/eui/es/components/icon/assets/arrow_right';
import { icon as arrowUpIcon } from '@elastic/eui/es/components/icon/assets/arrow_up';
import { icon as checkIcon } from '@elastic/eui/es/components/icon/assets/check';
import { icon as filterIcon } from '@elastic/eui/es/components/icon/assets/filter';
import { icon as iInCircleIcon } from '@elastic/eui/es/components/icon/assets/iInCircle';
import { icon as pencilIcon } from '@elastic/eui/es/components/icon/assets/pencil';
import { icon as starFilledIcon } from '@elastic/eui/es/components/icon/assets/star_filled';
import { icon as tokenKeyIcon } from '@elastic/eui/es/components/icon/assets/tokenKey';
import { appendIconComponentCache } from '@elastic/eui/es/components/icon/icon';

appendIconComponentCache({
arrowUp: arrowUpIcon,
arrowLeft: arrowLeftIcon,
arrowDown: arrowDownIcon,
arrowRight: arrowRightIcon,
iInCircle: iInCircleIcon,
tokenKey: tokenKeyIcon,
filter: filterIcon,
starFilled: starFilledIcon,
pencil: pencilIcon,
visualizeApp: visualizeAppIcon,
check: checkIcon,
});
70 changes: 70 additions & 0 deletions .storybook/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import path from 'path';

import type { StorybookConfig } from '@storybook/react-vite';
import { mergeConfig } from 'vite';

const config: StorybookConfig = {
stories: ['./stories/**/*.mdx', './stories/**/*.story.@(js|jsx|mjs|ts|tsx)'],
addons: [
// '@storybook/addon-docs',
'@storybook/addon-a11y',
'storybook-addon-toggles',
'@storybook/addon-themes',
],
framework: {
name: '@storybook/react-vite',
options: {
strictMode: false, // disable react strict mode
},
},
features: {
outline: false,
interactions: false,
},
viteFinal: (sbViteConfig) => {
return mergeConfig(sbViteConfig, {
resolve: {
alias: [
{
find: /^@elastic\/charts\/(.*)/,
replacement: path.resolve(__dirname, '../packages/charts/$1'),
},
{
find: '@elastic/charts',
replacement: path.resolve(__dirname, '../packages/charts/src/index.ts'),
},
{
// Need to resolve `@elastic/` sass imports in `@elastic/` packages from eui
find: 'node_modules/@elastic',
replacement: path.resolve(__dirname, '../node_modules/@elastic'),
},
{
// Alias for shared storybook utils, components and types
find: '@ech/sb',
replacement: path.resolve(__dirname, './shared'),
},
],
},
css: {
preprocessorOptions: {
scss: {
quietDeps: true, // silence divider deprecation warning messages
},
},
},
define: {
'process.env.NODE_ENV': JSON.stringify('development'),
},
});
},
};

export default config;
51 changes: 51 additions & 0 deletions .storybook/manager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import { addons } from 'storybook/manager-api';
import { create } from 'storybook/theming';

// Detect system theme preference
const getSystemTheme = () => {
if (typeof window !== 'undefined' && window.matchMedia) {
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
}
return 'light';
};

// Create themes for both light and dark
const lightTheme = create({
base: 'light',
brandTitle: 'Elastic Charts',
brandUrl: 'https://github.com/elastic/elastic-charts',
brandImage: 'logo-name.svg',
});

const darkTheme = create({
base: 'dark',
brandTitle: 'Elastic Charts',
brandUrl: 'https://github.com/elastic/elastic-charts',
brandImage: 'logo-name.svg',
});

// Set initial theme
addons.setConfig({
theme: getSystemTheme() === 'dark' ? darkTheme : lightTheme,
});

// Listen for system theme changes
if (typeof window !== 'undefined' && window.matchMedia) {
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');

const handleThemeChange = (e: MediaQueryListEvent) => {
addons.setConfig({
theme: e.matches ? darkTheme : lightTheme,
});
};

mediaQuery.addEventListener('change', handleThemeChange);
}
23 changes: 23 additions & 0 deletions .storybook/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"name": "charts-storybook-2",
"description": "Storybook demo for @elastic/charts library",
"license": "Apache-2.0",
"version": "0.0.0",
"type": "module",
"scripts": {
"start": "storybook dev -p 9001 -c ../.storybook --ci --no-version-updates",
"build": "rm -rf ../.out && storybook build -c ../.storybook -o ../.out",
"build:firebase": "storybook build -c ../.storybook -o ../e2e_server/public",
"typecheck": "tsc -p ./tsconfig.json --noEmit"
},
"devDependencies": {
"@storybook/addon-a11y": "^9.0.0",
"@storybook/addon-docs": "^9.0.0",
"@storybook/react-vite": "^9.0.0",
"@storybook/addon-themes": "^9.0.0",
"storybook-addon-toggles": "^0.0.8",
"storybook": "^9.0.0",
"typescript": "^5.5.3",
"vite": "^5.0.0"
}
}
Loading