Conditionally disable select option on Storybook control - reactjs

I have a React component with this props:
type MyComponentProps =
| {
orientation: 'horizontal';
labelPosition: 'labelRight' | 'labelDown';
}
| {
orientation: 'vertical';
labelPosition: 'labelRight';
};
When the prop orientation is vertical, it should only accept labelRight as the value for labelPosition (labelDown is only accepted when using the horizontal version).
I also have a story documentating each prop:
export default {
component: MyComponent,
argTypes: {{
orientation: {
control: {
type: 'select',
options: ['horizontal', 'vertical'],
},
},
labelPosition: {
control: {
type: 'select',
options: ['right', 'bottom'],
},
},
}}
}
Is it possible to conditionally disable an option in Storybook? I tried with conditional controls but it disables the whole control.

Related

How can you set custom labels for controls in Storybook?

I'm trying to set custom labels for my controls in Storybook as outlined in the instructions here, but it's not working as expected. According to the instructions you can specify control.labels to configure custom labels for your checkbox, radio, or select input.
Right now I have a prop of size that allows the user to select the size of the component, but in Storybook it's showing the number value as opposed to name. e.g.
Instead of the number values I want the labels to read the names from the enum below.
export enum sizes {
small = 32,
default = 50,
large = 100,
};
How can I update Storybook to use the enum sizes name instead of the value?
// storybook
export default {
title: 'Components/Spinner',
component: Spinner,
controls: { expanded: true },
argTypes: {
type: {
options: ['primary', 'secondary', 'success', 'warning', 'danger', 'info', 'light'],
control: { type: 'radio'},
},
size: {
options: [sizes.default, sizes.small, sizes.large],
control: {
type: 'radio',
labels: {
Default: 'Default',
Small: 'Small',
Large: 'Large'
},
},
}
}
} as Meta;
FYI: If I update options to the following:
options: sizes,
I get both the name and the value and only the name works
In case anyone else comes across this issue I solved it by manually typing in the values. According to this Stackoverflow post enums end up as object. So it was outputting both the key and values.
export default {
title: 'Components/Spinner',
component: Spinner,
controls: { expanded: true },
argTypes: {
type: {
options: ['primary', 'secondary', 'success', 'warning', 'danger', 'info', 'light'],
control: { type: 'radio'},
},
size: {
options: [32, 50, 100],
control: {
type: 'radio',
labels: {
32: 'small',
50: 'default',
100: 'large',
},
},
}
}
} as Meta;

Switch storybook background through global parameter

I have the following globalTypes to enable a toolbar in storybook that lets me select the theme:
export const globalTypes = {
theme: {
name: 'Theme',
description: 'Global theme',
defaultValue: MyTheme.Light,
toolbar: {
icon: 'mirror',
items: [MyTheme.Light, MyTheme.Dark],
showName: true,
dynamicTitle: true,
},
},
};
This works fine and I can switch the theme through the toolbar:
Now I want to set the background color of the story (background-color of the body) according to the theme, but I cannot figure out how to do that for all stories globally.
I know how to configure different background colors, but I have no idea how to switch them based on the theme set in context.globals. How does this work?
You can use decorators to set global view and se
Like here
import { useEffect } from "react";
import "./preview.css";
enum MyTheme {
Light = "light",
Dark = "dark",
Tomato = "tomato"
}
export const globalTypes = {
theme: {
name: "Theme",
description: "Global theme",
defaultValue: MyTheme.Light,
toolbar: {
icon: "mirror",
items: [
{
title: "light",
value: MyTheme.Light
},
{ title: "dark", value: MyTheme.Dark },
{ title: "tomato", value: MyTheme.Tomato }
],
showName: true,
dynamicTitle: true
}
}
};
const clearStyles = (element: HTMLElement) => {
for (const className of Object.values(MyTheme)) {
element.classList.remove(className);
}
};
const applyStyle = (element: HTMLElement, className: string) => {
element.classList.add(className);
};
const WithThemeProvider = (Story, context) => {
useEffect(() => {
const body = window.document.body;
clearStyles(body);
applyStyle(body, context.globals.theme);
return () => {
clearStyles(body);
};
}, [context.globals.theme]);
return <Story />;
};
export const decorators = [WithThemeProvider];
I know it might feel "dirty" to work directly with body. But it is suggested way for instance addons decorator.

Storybook does not infer controls for Kendo-UI (React) Components

I'm in the process of creating an internal component library using Storybook and Kendo React and I'm having to explicitly define argTypes for controls for every prop in order to get them to give me anything but a string input (with the sole exception of booleans).
Here's a sample of one of my stories:
import React from "react";
import { ComponentStory, ComponentMeta } from "#storybook/react";
import { TestAvatar } from "../../components/Avatar/TestAvatar";
export default {
title: "Atomic Components/Test Avatar",
component: TestAvatar,
} as ComponentMeta<typeof TestAvatar>;
const Template: ComponentStory<typeof TestAvatar> = ({ ...args }: any) => (
<TestAvatar {...args} />
);
export const Standard = Template.bind({});
Standard.args = {
border: false,
themeColor: "base",
fillMode: "solid",
rounded: "medium",
size: "medium",
type: undefined,
};
Standard.argTypes = {
themeColor: {
options: ["base", "primary", "secondary", "tertiary", "success", "error"],
control: { type: "select" },
},
rounded: {
options: ["small", "medium", "large", "full", null],
control: { type: "select" },
},
size: {
options: ["small", "medium", "large"],
control: { type: "select" },
},
fillMode: {
options: ["solid", "outline"],
control: { type: "radio" },
},
type: {
options: ["image", "text", "icon"],
control: { type: "select" },
},
};
I have all the standard addons, although I haven't created any typescript options in main.js - I've tried, using info I got from this question but they don't seem to make any difference.
I've tried a few things, using Story and Meta from Storybook instead of the newer ComponentStory and ComponentMeta, I've tried extending the AvatarProps interface and I have also tried using the Kendo AvatarProps interface directly, but I can't find a way around it.
I suspect it's to do with how the Kendo library is typed.
I want to have to avoid creating controls for every single component I'm creating - does anyone have any ideas on how I can circumvent that amount of unnecessary work?

Using chart js options with react-chartjs-2 and typescript

I am trying to use a plugin with a react-chartjs doughnut chart. In order to use the plugin (https://www.npmjs.com/package/#scottalan/chartjs-plugin-doughnutlabel), I have to pass options to the component. But when I try to pass options, I get a type error
Type '{ doughnutlabel: { labels: { text: string; font: { size: string; family: string; style: string; weight: string; }; color: string; }[]; }; }' is not assignable to type '_DeepPartialObject<PluginOptionsByType<keyof ChartTypeRegistry>>'.
Object literal may only specify known properties, and 'doughnutlabel' does not exist in type '_DeepPartialObject<PluginOptionsByType<keyof ChartTypeRegistry>>'.
My best guess is that ChartOptions is the wrong type to assign my options to because I get the same error event without trying to use the plugin with const options: ChartOptions = {}. My full code is below, any help is appreciated.
import React, { useEffect, ReactNode, Component } from "react"
import { Chart, ChartOptions, registerables } from 'chart.js'
import { Doughnut } from 'react-chartjs-2';
Chart.register(...registerables);
const MyDoughnut = (props: ComponentProps) => {
const renderChart = (percents: number[]) => {
const data = {
datasets: [{
label: 'Progress',
data: percents,
backgroundColor: [
'rgb(255, 99, 132)',
'transparent',
],
hoverOffset: 4,
cutout: "75%",
radius: "100%"
}]
}
const options: ChartOptions = {
responsive: true,
legend: {
display: false,
position: 'top',
},
title: {
display: true,
fontSize: 20,
text: 'My Title'
},
plugins: {
doughnutlabel: {
labels: [
{
text: "Foo",
font: {
size: '60',
family: 'Arial, Helvetica, sans-serif',
style: 'italic',
weight: 'bold'
},
color: '#bc2c1a'
}
]
}
}
}
return <Doughnut options={options} data={data}></Doughnut>
}
const render = (): ReactNode => {
// const percents = props.args["percents"]
const percents = [76, 24];
return (
<span>
{renderChart(percents)}
</span>
)
}
return <div>{render()}</div>
};
I am using react-chartjs-2 version 4.0.0 and react version 17.0.2.
It turns out the typing error can be solved by using any instead of ChartOptions.
const options: any = {
...
}
As LeeLenalee pointed out, there is also an issue using this specific plugin with V3 of Chart.js, but using any worked with another similar plugin.
types are exported from chart.js
import type { ChartData, ChartOptions } from 'chart.js';
interface DoughnutProps {
options: ChartOptions<'doughnut'>;
data: ChartData<'doughnut'>;
}
I looked at the source of the plugin and it seems like even if you solve the typing issue it wont work. The reason for this is that you are using V3 of Chart.js while the plugin was written for V2 and never updated. It is self registering and it does this in the wrong way, it defines its default options in the wrong place (both namespaces dont exist anymore).
So if you really want to use this plugin I suggest you take a look at the source and modify it so it works with V3 or downgrade to chart.js v2 where there is no build in typing or find another plugin.

Material-UI - Can I selectively enable Button ripple?

I know I can turn off all ripples by putting this in the theme object:
components: {
MuiButtonBase: {
defaultProps: {
disableRipple: true,
},
},
},
I know I can turn it off for everything except all IconButtons with the following:
components: {
MuiButtonBase: {
defaultProps: {
disableRipple: true,
},
},
MuiIconButton: {
defaultProps: {
disableRipple: false,
},
},
},
I know I can turn it off each component individually with this JSX:
<Button disableRipple>
Only one rippleless!
</Button>
But is there any way to turn the ripple off globally and just enable it individually?
For example, something that would do what the following fake code suggests:
<Button enableRipple>
Only one rippled!
</Button>
🤔
Okay its this easy:
<Button disableRipple={false}>
Only one rippled!
</Button>

Resources