Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ import React from 'react';

import { ResetWrapper } from 'storybook/internal/components';

import type { Meta, StoryObj } from '@storybook/react-vite';

import { expect, screen } from 'storybook/test';

import { ArgRow } from './ArgRow';
import { TableWrapper } from './ArgsTable';

Expand All @@ -10,7 +14,7 @@ export default {
title: 'Components/ArgsTable/ArgRow',

decorators: [
(getStory: any) => (
(getStory) => (
<ResetWrapper>
<TableWrapper>
<tbody>{getStory()}</tbody>
Expand All @@ -21,7 +25,7 @@ export default {
argTypes: {
updateArgs: { action: 'updateArgs' },
},
};
} satisfies Meta<typeof ArgRow>;

export const String = {
args: {
Expand Down Expand Up @@ -309,6 +313,52 @@ export const Func = {
},
};

export const LongFunc = {
args: {
row: {
...Func.args.row,
table: {
...Func.args.row.table,
defaultValue: {
summary: 'func',
detail: `(a, b) => {
// Calculate various metrics between a and b
const lengthA = a.length;
const lengthB = b.length;

// Determine the longer string
const maxLength = Math.max(lengthA, lengthB);
const minLength = Math.min(lengthA, lengthB);

// Calculate similarity score
let matchCount = 0;
for (let i = 0; i < minLength; i++) {
if (a[i] === b[i]) {
matchCount++;
}
}

// Compute weighted average
const similarity = (matchCount / maxLength) * 100;

// Generate result string
const result = \`Similarity: \${similarity.toFixed(2)}%, Length diff: \${Math.abs(lengthA - lengthB)}\`;

return result;
}`,
},
},
},
},
play: async ({ canvas }) => {
const funcButton = canvas.getByRole('button', { name: 'func' });
funcButton.click();
expect(
await screen.findByText(/Calculate various metrics between a and b/)
).toBeInTheDocument();
},
} satisfies StoryObj<typeof ArgRow>;

const enumeration =
'"search" | "arrow-to-bottom" | "arrow-to-right" | "bell" | "check" | "check-circle"';
export const Enum = {
Expand Down Expand Up @@ -345,7 +395,20 @@ export const LongEnum = {
},
},
},
};
play: async ({ canvas, step }) => {
await step('Expand long enum', async () => {
canvas.getByRole('button', { name: 'Show 26 more...' }).click();
expect(await canvas.findByText(/exclamation-triangle/)).toBeVisible();
});
await step('Collapse long enum', async () => {
(await canvas.findByRole('button', { name: 'Show less...' })).click();
expect(await canvas.findByText(/exclamation-triangle/)).not.toBeVisible();
});
await step('Re-expand for visual test', async () => {
(await canvas.findByRole('button', { name: 'Show 26 more...' })).click();
});
},
} satisfies StoryObj<typeof ArgRow>;

const complexUnion =
'((a: string | SVGSVGElement) => void) | RefObject<SVGSVGElement | number> | [a|b] | {a|b}';
Expand Down
88 changes: 60 additions & 28 deletions code/addons/docs/src/blocks/components/ArgsTable/ArgValue.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import type { FC } from 'react';
import React, { useState } from 'react';

import { SyntaxHighlighter, TooltipProvider, codeCommon } from 'storybook/internal/components';
import { PopoverProvider, SyntaxHighlighter, codeCommon } from 'storybook/internal/components';

import { ChevronSmallDownIcon, ChevronSmallUpIcon } from '@storybook/icons';

import { uniq } from 'es-toolkit/array';
import memoize from 'memoizerific';
import { styled } from 'storybook/theming';

import type { PropSummaryValue } from './types';
Expand Down Expand Up @@ -37,6 +36,27 @@ const Summary = styled.div<{ isExpanded?: boolean }>(({ isExpanded }) => ({
minWidth: 100,
}));

const DetailsContainer = styled.details({
display: 'flex',
flexDirection: 'column',
summary: {
order: 2,
},
'summary::-webkit-details-marker': {
display: 'none',
},
'summary::marker': {
content: 'none',
},
});

const AlignedDetails = styled.div({
display: 'flex',
flexDirection: 'column',
flexWrap: 'wrap',
alignItems: 'flex-start',
});

const Text = styled.span<{ simple?: boolean }>(codeCommon, ({ theme, simple = false }) => ({
flex: '0 0 auto',
fontFamily: theme.typography.fonts.mono,
Expand All @@ -57,29 +77,40 @@ const Text = styled.span<{ simple?: boolean }>(codeCommon, ({ theme, simple = fa
}),
}));

const ExpandButton = styled.button(({ theme }) => ({
const ExpandButton = styled.summary(({ theme }) => ({
fontFamily: theme.typography.fonts.mono,
color: theme.color.secondary,
marginBottom: '4px',
cursor: 'pointer',
lineHeight: 'normal',
margin: '0 0 4px',
padding: '1px 6px',
background: 'none',
border: 'none',
}));

const Expandable = styled.div(codeCommon, ({ theme }) => ({
const Expandable = styled.button(codeCommon, ({ theme }) => ({
fontFamily: theme.typography.fonts.mono,
color: theme.color.secondary,
fontSize: theme.typography.size.s1, // overrides codeCommon
margin: 0,
whiteSpace: 'nowrap',
display: 'flex',
alignItems: 'center',
cursor: 'pointer',
'&:hover': {
border:
theme.base === 'light' ? '1px solid hsl(0 0 0 / 0.15)' : '1px solid hsl(0 0 100 / 0.15)',
},
'&:focus-visible': {
outline: `2px solid ${theme.color.secondary}`,
outlineOffset: '2px',
},
}));

const Detail = styled.div<{ width: string }>(({ theme, width }) => ({
width,
const Detail = styled.div(({ theme }) => ({
Comment thread
Sidnioulz marked this conversation as resolved.
minWidth: 200,
maxWidth: 800,
padding: 15,
paddingRight: 16,
// Don't remove the mono fontFamily here even if it seems useless, this is used by the browser to calculate the length of a "ch" unit.
fontFamily: theme.typography.fonts.mono,
fontSize: theme.typography.size.s1,
Expand Down Expand Up @@ -107,12 +138,6 @@ const ArgText: FC<ArgTextProps> = ({ text, simple }) => {
return <Text simple={simple}>{text}</Text>;
};

const calculateDetailWidth = memoize(1000)((detail: string): string => {
const lines = detail.split(/\r?\n/);

return `${Math.max(...lines.map((x) => x.length))}ch`;
});

const getSummaryItems = (summary: string) => {
if (!summary) {
return [summary];
Expand All @@ -123,13 +148,16 @@ const getSummaryItems = (summary: string) => {
return uniq(summaryItems);
};

const renderSummaryItems = (summaryItems: string[], isExpanded = true) => {
let items = summaryItems;
if (!isExpanded) {
items = summaryItems.slice(0, ITEMS_BEFORE_EXPANSION);
}
const renderSummaryItems = (summaryItems: string[]) => {
return summaryItems
.slice(0, ITEMS_BEFORE_EXPANSION)
.map((item) => <ArgText key={item} text={item === '' ? '""' : item} />);
};

return items.map((item) => <ArgText key={item} text={item === '' ? '""' : item} />);
const renderExpandedItems = (summaryItems: string[]) => {
return summaryItems
.slice(ITEMS_BEFORE_EXPANSION)
.map((item) => <ArgText key={item} text={item === '' ? '""' : item} />);
};

const ArgSummary: FC<ArgSummaryProps> = ({ value, initialExpandedArgs }) => {
Expand Down Expand Up @@ -160,25 +188,29 @@ const ArgSummary: FC<ArgSummaryProps> = ({ value, initialExpandedArgs }) => {

return hasManyItems ? (
<Summary isExpanded={isExpanded}>
{renderSummaryItems(summaryItems, isExpanded)}
<ExpandButton onClick={() => setIsExpanded(!isExpanded)}>
{isExpanded ? 'Show less...' : `Show ${itemsCount - ITEMS_BEFORE_EXPANSION} more...`}
</ExpandButton>
{renderSummaryItems(summaryItems)}
<DetailsContainer open={isExpanded} onToggle={(e) => setIsExpanded(e.currentTarget.open)}>
<AlignedDetails>{renderExpandedItems(summaryItems)}</AlignedDetails>
<ExpandButton role="button">
Comment thread
Sidnioulz marked this conversation as resolved.
{isExpanded ? 'Show less...' : `Show ${itemsCount - ITEMS_BEFORE_EXPANSION} more...`}
</ExpandButton>
</DetailsContainer>
</Summary>
) : (
<Summary>{renderSummaryItems(summaryItems)}</Summary>
);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}

return (
<TooltipProvider
<PopoverProvider
placement="bottom"
visible={isOpen}
onVisibleChange={(isVisible) => {
setIsOpen(isVisible);
}}
tooltip={
<Detail width={calculateDetailWidth(detail)}>
hasCloseButton
popover={
<Detail>
<SyntaxHighlighter language="jsx" format={false}>
{detail}
</SyntaxHighlighter>
Expand All @@ -189,7 +221,7 @@ const ArgSummary: FC<ArgSummaryProps> = ({ value, initialExpandedArgs }) => {
<span>{summaryAsString}</span>
{isOpen ? <ChevronUpIcon /> : <ChevronDownIcon />}
</Expandable>
</TooltipProvider>
</PopoverProvider>
);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ export const PopoverProvider = ({
},
[onVisibleChange]
);
const onHide = useCallback(() => setIsOpen(false), []);
const onHide = useCallback(() => onOpenChange(false), [onOpenChange]);

return (
<DialogTrigger
Expand Down