如何在Storybook中测试全局CSS定制的Ionic组件?
Ionic自定义组件的Storybook测试方案
问题背景
我希望用Storybook隔离测试自定义Ionic组件,把大部分定制逻辑放在全局CSS里,不用创建大量新组件,但不知道怎么在Storybook里测试。现有可运行的Story代码和全局SCSS如下:
现有Story代码
import { centered, in_header } from '~/.storybook/utils'; import { componentWrapperDecorator } from '@storybook/angular'; import type { Meta, StoryObj } from '@storybook/angular'; import { IonButton } from '@ionic/angular'; type Story = StoryObj<IonButton>; const meta: Meta<IonButton> = { title: 'Ionic/Monkey Buttons', component: IonButton, tags: ['autodocs'], decorators: [ centered({ horizontalMarginPx: 20 }), componentWrapperDecorator(() => ` <ion-button> My Button </ion-button> `), ] }; export default meta; export const PrimaryFull: Story = { }; export const PrimarySecondary: Story = { args: { // label: 'My Button', color: 'tertiary' } };
全局SCSS定制
ion-button { // Rounded Corners --border-radius: var(--button-radius); // Defaults display: inline-flex; // Make sure button doesn't take full width by default align-items: center; justify-content: center; text-transform: none !important; // Disable uppercase globally for ion-buttons (need to force it!) padding: 0; // Ionic adds 5px padding top & bottom margin: 0; // remove outer margin (beyond the border-box) box-sizing: border-box !important; // Icon &.icon-present { .button-text { margin-left: 8px; text-align: left; text-transform: none !important; } ion-icon { margin-left: auto; } } &.icon-absent { .button-text { text-align: center; margin: 0 auto; text-transform: none !important; } } }
具体问题解答
1. 如何在Story中为ion-button添加自定义类(如icon-present)
有两种常用实现方式:
- 方式一:通过Story的
args传递class属性
Ionic组件支持直接绑定class属性,在Story的args中定义即可:export const ButtonWithIconClass: Story = { args: { class: 'icon-present', color: 'primary' } }; - 方式二:修改
componentWrapperDecorator的模板
直接在装饰器的HTML模板中给ion-button添加类名,单个Story可单独设置装饰器覆盖全局配置:export const ButtonWithIconClass: Story = { decorators: [ componentWrapperDecorator(() => ` <ion-button class="icon-present"> My Button </ion-button> `) ] };
2. 如何在Story层面修改按钮文本
三种灵活实现方式:
- 方式一:修改
componentWrapperDecorator的模板文本
直接替换装饰器HTML中的硬编码文本:export const CustomTextButton: Story = { decorators: [ componentWrapperDecorator(() => ` <ion-button> 自定义按钮文本 </ion-button> `) ] }; - 方式二:利用Ionic按钮的
label输入属性IonButton原生支持label输入属性,直接在args中传递即可,需先移除全局装饰器中的硬编码文本避免冲突:// 全局装饰器修改为无文本模板 componentWrapperDecorator(() => `<ion-button></ion-button>`), // 单个Story设置文本 export const CustomTextButton: Story = { args: { label: '自定义按钮文本' } }; - 方式三:通过
template字段自定义按钮结构
给单个Story设置专属模板,完全自定义内容:export const CustomTextButton: Story = { template: `<ion-button> {{ label }} </ion-button>`, args: { label: '自定义按钮文本' } };
额外需求:通过CSS自动判断是否含ion-icon并添加类
纯CSS没有父选择器,无法直接实现“检测子元素后给父元素加类”的逻辑,但有两种替代方案:
方案一:用CSS直接匹配含ion-icon的按钮
不需要额外类名,用:has()选择器直接匹配含ion-icon的按钮,现代浏览器和Storybook预览环境均支持:
// 替换原有.icon-present和.icon-absent逻辑 ion-button:has(ion-icon) { .button-text { margin-left: 8px; text-align: left; text-transform: none !important; } ion-icon { margin-left: auto; } } ion-button:not(:has(ion-icon)) { .button-text { text-align: center; margin: 0 auto; text-transform: none !important; } }
方案二:给不同场景的Story设置专属包裹模板
如果坚持要用类名,可给每个场景的Story单独设置componentWrapperDecorator,完全可行:
// 带图标的按钮Story export const ButtonWithIcon: Story = { decorators: [ componentWrapperDecorator(() => ` <ion-button class="icon-present"> <ion-icon name="star"></ion-icon> <span class="button-text">带图标按钮</span> </ion-button> `) ] }; // 不带图标的按钮Story export const ButtonWithoutIcon: Story = { decorators: [ componentWrapperDecorator(() => ` <ion-button class="icon-absent"> <span class="button-text">纯文本按钮</span> </ion-button> `) ] };
内容的提问来源于stack exchange,提问作者monkey
相关产品推荐
相关产品推荐

