Skip to content

Commit b7ff818

Browse files
committed
fix: ensure story wrapper is resolved correctly
- uses Story wrapper for ComboBox story as example
1 parent 156f277 commit b7ff818

3 files changed

Lines changed: 48 additions & 22 deletions

File tree

packages/eui/.storybook/addons/code-snippet/decorators/render_jsx.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -164,8 +164,6 @@ export const renderJsx = (
164164
: // @ts-expect-error (Converted from ts-ignore)
165165
reactElementToJSXString.default;
166166

167-
const shouldResolveChildren =
168-
context?.parameters?.codeSnippet?.resolveChildren === true;
169167
const shouldResolveStoryElementOnly =
170168
context?.parameters?.codeSnippet?.resolveStoryElementOnly === true;
171169

@@ -175,15 +173,17 @@ export const renderJsx = (
175173
// manual flag to remove an outer story wrapper and resolve its children instead
176174
// useful when complex custom stories are build where the actual story component is
177175
// not the outer component but part of a composition within another wrapper
178-
if (shouldResolveChildren) {
179-
node = getResolvedStoryChild(child, context);
180-
// resolves the story element only and removes any wrapper or siblings
181-
} else if (shouldResolveStoryElementOnly) {
176+
if (shouldResolveStoryElementOnly) {
182177
const storyNode = getStoryComponent(child, context);
183178

184179
if (storyNode) {
185180
node = storyNode;
186181
}
182+
// manual flag to remove an outer story wrapper and resolve its children instead
183+
// useful when complex custom stories are build where the actual story component is
184+
// not the outer component but part of a composition within another wrapper
185+
} else if (isStoryWrapper(child, context)) {
186+
node = getResolvedStoryChild(child, context);
187187
} else {
188188
// removes outer wrapper components but leaves:
189189
// - stateful wrappers (kept and renamed later via displayName)

packages/eui/.storybook/addons/code-snippet/decorators/utils.ts

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
ComponentType,
1616
ExoticComponent,
1717
isValidElement,
18+
Component,
1819
} from 'react';
1920
import { Args, StoryContext } from '@storybook/react';
2021
import * as prettier from 'prettier';
@@ -56,8 +57,11 @@ export const getElementDisplayName = (
5657
node: ReactElement
5758
): string | undefined => {
5859
let displayName;
60+
const isClassComponent = node instanceof Component;
5961

60-
if (typeof node.type === 'function' || typeof node.type === 'object') {
62+
if (isClassComponent) {
63+
displayName = node.constructor.name;
64+
} else if (typeof node.type === 'function' || typeof node.type === 'object') {
6165
const component = node.type as FunctionComponent;
6266
displayName = component.displayName ?? component.name ?? undefined;
6367
}
@@ -92,10 +96,10 @@ export const getEmotionComponentDisplayName = (
9296
: emotionTypeName ?? emotionLabelData?.displayName;
9397

9498
// remove internal component underscore markings
95-
return replacementName ? replacementName.replace('_', '') : displayName;
99+
return replacementName ?? displayName;
96100
}
97101

98-
return displayName;
102+
return displayName ? displayName.replace('_', '') : displayName;
99103
};
100104

101105
export const getStoryComponentDisplayName = (
@@ -127,15 +131,18 @@ export const isStoryComponent = (
127131
): boolean => {
128132
if (!context) return false;
129133

130-
const displayName = getEmotionComponentDisplayName(node)?.replace(/^_/, '');
134+
const isClassComponent = node instanceof Component;
135+
const displayName = isClassComponent
136+
? node.constructor?.name
137+
: getEmotionComponentDisplayName(node)?.replace(/^_/, '');
131138
const isCurrentStory = displayName
132139
? displayName === context?.component?.displayName
133140
: false;
134141

135142
return isCurrentStory;
136143
};
137144

138-
const isStoryWrapper = (node: ReactElement, context: StoryContext) => {
145+
export const isStoryWrapper = (node: ReactElement, context: StoryContext) => {
139146
const displayName = getEmotionComponentDisplayName(node);
140147
const isStoryWrapper =
141148
(typeof displayName === 'string' && displayName.startsWith('Story')) ||
@@ -210,9 +217,15 @@ export const getResolvedStoryChild = (
210217
child: ReactElement,
211218
context: StoryContext
212219
) => {
213-
return child.type && typeof child.type === 'function'
214-
? (child.type as (args: Args) => ReactElement)(context?.args)
215-
: child;
220+
if (!child.type) return child;
221+
if (typeof child.type !== 'function') return child;
222+
223+
const isClassComponent = child.type.prototype instanceof Component;
224+
const resolvedChild = isClassComponent
225+
? child
226+
: (child.type as (args: Args) => ReactElement)(context?.args);
227+
228+
return resolvedChild;
216229
};
217230

218231
/**
@@ -245,9 +258,15 @@ export const getStoryComponent = (
245258
// skip non-ReactElement children
246259
if (!isValidElement(child)) continue;
247260

261+
const displayName = getEmotionComponentDisplayName(child);
248262
// Story wrappers need to be resolved first to ensure the right data
249263
const resolvedChild = getResolvedStoryChild(child, context);
250-
resolveChildren(resolvedChild);
264+
const resolvedDisplayName =
265+
getEmotionComponentDisplayName(resolvedChild);
266+
267+
if (resolvedDisplayName !== displayName) {
268+
resolveChildren(resolvedChild);
269+
}
251270
}
252271
} else if (
253272
// CASE: story wrapper; no children
@@ -260,15 +279,15 @@ export const getStoryComponent = (
260279
const resolvedDisplayName =
261280
getEmotionComponentDisplayName(resolvedChild);
262281

263-
if (resolvedDisplayName && resolvedDisplayName !== displayName) {
282+
if (resolvedDisplayName !== displayName) {
264283
resolveChildren(resolvedChild);
265284
}
266285
} else if (
267286
// CASE: single child element
268287
childNode.props?.children &&
269288
!Array.isArray(childNode.props?.children)
270289
) {
271-
resolveChildren(childNode.props?.children);
290+
storyNode = childNode.props.children;
272291
}
273292
}
274293
};
@@ -302,9 +321,11 @@ export const getDefaultPropsfromDocgenInfo = (
302321

303322
if (!storyComponent) return undefined;
304323

305-
let propsInfo = isEmotionComponent(storyComponent)
306-
? storyComponent.props?.[EMOTION_TYPE_KEY]?.__docgenInfo.props
307-
: storyComponent.type?.__docgenInfo?.props;
324+
let propsInfo =
325+
isEmotionComponent(storyComponent) &&
326+
typeof storyComponent.props?.[EMOTION_TYPE_KEY] !== 'string'
327+
? storyComponent.props?.[EMOTION_TYPE_KEY]?.__docgenInfo.props
328+
: storyComponent.type?.__docgenInfo?.props;
308329

309330
const args = context.args;
310331

packages/eui/src/components/combo_box/combo_box.stories.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,12 @@ export const WithTooltip: Story = {
133133
hideStorybookControls(WithTooltip, ['onChange']);
134134

135135
export const CustomMatcher: Story = {
136-
render: (args) => <StatefulCustomMatcher {...args} />,
136+
parameters: {
137+
codeSnippet: {
138+
resolveStoryElementOnly: true,
139+
},
140+
},
141+
render: (args) => <StoryCustomMatcher {...args} />,
137142
};
138143

139144
export const Groups: Story = {
@@ -237,7 +242,7 @@ const StatefulComboBox = ({
237242
);
238243
};
239244

240-
const StatefulCustomMatcher = ({
245+
const StoryCustomMatcher = ({
241246
singleSelection,
242247
onChange,
243248
...args

0 commit comments

Comments
 (0)