Skip to content

Commit

Permalink
Merge pull request #29453 from jsingh0026/fix/external-storybook-comp…
Browse files Browse the repository at this point in the history
…osition

Composition: Fix composed story search
  • Loading branch information
yannbf authored Nov 15, 2024
2 parents 33e4397 + 39601d1 commit 9955a0b
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import React from 'react';
import { IconButton } from '@storybook/core/components';
import { styled } from '@storybook/core/theming';
import { BottomBarToggleIcon, MenuIcon } from '@storybook/icons';
import type { API_IndexHash, API_Refs } from '@storybook/types';

import { useStorybookApi, useStorybookState } from '@storybook/core/manager-api';

Expand All @@ -17,27 +18,46 @@ interface MobileNavigationProps {
showPanel: boolean;
}

// Function to combine all indexes
function combineIndexes(rootIndex: API_IndexHash | undefined, refs: API_Refs) {
// Create a copy of the root index to avoid mutation
const combinedIndex = { ...(rootIndex || {}) }; // Use an empty object as fallback

// Traverse refs and merge each nested index with the root index
Object.values(refs).forEach((ref) => {
if (ref.index) {
Object.assign(combinedIndex, ref.index);
}
});

return combinedIndex;
}

/**
* Walks the tree from the current story to combine story+component+folder names into a single
* string
*/
const useFullStoryName = () => {
const { index } = useStorybookState();
const { index, refs } = useStorybookState();
const api = useStorybookApi();
const currentStory = api.getCurrentStoryData();

if (!currentStory) {
return '';
}

const combinedIndex = combineIndexes(index, refs || {});
let fullStoryName = currentStory.renderLabel?.(currentStory, api) || currentStory.name;
// @ts-expect-error (non strict)
let node = index[currentStory.id];

// @ts-expect-error (non strict)
while ('parent' in node && node.parent && index[node.parent] && fullStoryName.length < 24) {
// @ts-expect-error (non strict)
node = index[node.parent];
let node = combinedIndex[currentStory.id];

while (
node &&
'parent' in node &&
node.parent &&
combinedIndex[node.parent] &&
fullStoryName.length < 24
) {
node = combinedIndex[node.parent];
const parentName = node.renderLabel?.(node, api) || node.name;
fullStoryName = `${parentName}/${fullStoryName}`;
}
Expand Down
60 changes: 56 additions & 4 deletions code/e2e-tests/composition.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,10 @@ test.describe('composition', () => {
'Slow, framework independent test, so only run it on in react-vite/default-ts'
);

test.beforeEach(async ({ page }) => {
test('should filter and render composed stories', async ({ page }) => {
await page.goto(storybookUrl);
await new SbPage(page, expect).waitUntilLoaded();
});

test('should correctly filter composed stories', async ({ page }) => {
// Expect that composed Storybooks are visible
await expect(page.getByTitle('Storybook 8.0.0')).toBeVisible();
await expect(page.getByTitle('Storybook 7.6.18')).toBeVisible();
Expand All @@ -35,10 +33,64 @@ test.describe('composition', () => {
// Expect composed stories `to be available in the search
await page.getByPlaceholder('Find components').fill('Button');
await expect(
page.getByRole('option', { name: 'Button Storybook 8.0.0 / @blocks / examples' })
page.getByRole('option', { name: 'Button Storybook 7.6.18 / @blocks / examples' })
).toBeVisible();

const buttonStory = page.getByRole('option', {
name: 'Button Storybook 8.0.0 / @blocks / examples',
});
await expect(buttonStory).toBeVisible();
await buttonStory.click();

// Note: this could potentially be flaky due to it accessing a hosted Storybook
await expect(
page
.locator('iframe[title="storybook-ref-storybook\\@8\\.0\\.0"]')
.contentFrame()
.getByRole('heading', { name: 'Example button component' })
).toBeVisible({ timeout: 15000 });
});

test('should filter and render composed stories on mobile', async ({ page }) => {
page.setViewportSize({ width: 320, height: 800 });
await page.goto(storybookUrl);
await new SbPage(page, expect).waitUntilLoaded();

await page.click('button[title="Open navigation menu"]');

// Expect that composed Storybooks are visible
await expect(page.getByTitle('Storybook 8.0.0')).toBeVisible();
await expect(page.getByTitle('Storybook 7.6.18')).toBeVisible();

// Expect composed stories to be available in the sidebar
await page.locator('[id="storybook\\@8\\.0\\.0_components-badge"]').click();
await expect(
page.locator('[id="storybook\\@8\\.0\\.0_components-badge--default"]')
).toBeVisible();

await page.locator('[id="storybook\\@7\\.6\\.18_components-badge"]').click();
await expect(
page.locator('[id="storybook\\@7\\.6\\.18_components-badge--default"]')
).toBeVisible();

// Expect composed stories `to be available in the search
await page.getByPlaceholder('Find components').fill('Button');
await expect(
page.getByRole('option', { name: 'Button Storybook 7.6.18 / @blocks / examples' })
).toBeVisible();

const buttonStory = page.getByRole('option', {
name: 'Button Storybook 8.0.0 / @blocks / examples',
});
await expect(buttonStory).toBeVisible();
await buttonStory.click();

// Note: this could potentially be flaky due to it accessing a hosted Storybook
await expect(
page
.locator('iframe[title="storybook-ref-storybook\\@8\\.0\\.0"]')
.contentFrame()
.getByRole('heading', { name: 'Example button component' })
).toBeVisible({ timeout: 15000 });
});
});

0 comments on commit 9955a0b

Please sign in to comment.