diff --git a/CHANGELOG.md b/CHANGELOG.md index 990f03183105..26e490d7c6d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## 7.4.2 + +- Addon API: Improve the updateStatus API - [#24007](https://github.com/storybookjs/storybook/pull/24007), thanks [@ndelangen](https://github.com/ndelangen)! +- Nextjs: Migrate from config to previewAnnotations - [#24178](https://github.com/storybookjs/storybook/pull/24178), thanks [@yannbf](https://github.com/yannbf)! +- UI: Fix SVG override fill when path has a fill attribute - [#24156](https://github.com/storybookjs/storybook/pull/24156), thanks [@ndelangen](https://github.com/ndelangen)! +- UI: Improve look and feel of status UI in sidebar - [#24099](https://github.com/storybookjs/storybook/pull/24099), thanks [@ndelangen](https://github.com/ndelangen)! + ## 7.4.1 - CLI: Add uncaughtException handler - [#24018](https://github.com/storybookjs/storybook/pull/24018), thanks [@yannbf](https://github.com/yannbf)! diff --git a/code/frameworks/nextjs/src/preset.ts b/code/frameworks/nextjs/src/preset.ts index 238ba2bb170e..4aa06d54c4d0 100644 --- a/code/frameworks/nextjs/src/preset.ts +++ b/code/frameworks/nextjs/src/preset.ts @@ -68,7 +68,7 @@ export const core: PresetProperty<'core', StorybookConfig> = async (config, opti }; }; -export const config: StorybookConfig['previewAnnotations'] = (entry = []) => [ +export const previewAnnotations: StorybookConfig['previewAnnotations'] = (entry = []) => [ ...entry, require.resolve('@storybook/nextjs/preview.js'), ]; diff --git a/code/lib/manager-api/src/modules/stories.ts b/code/lib/manager-api/src/modules/stories.ts index c69e5e23cdb5..aff85aabcea3 100644 --- a/code/lib/manager-api/src/modules/stories.ts +++ b/code/lib/manager-api/src/modules/stories.ts @@ -263,7 +263,10 @@ export interface SubAPI { * @param {StatusUpdate} update - An object containing the updated status information. * @returns {Promise} A promise that resolves when the status has been updated. */ - experimental_updateStatus: (addonId: string, update: API_StatusUpdate) => Promise; + experimental_updateStatus: ( + addonId: string, + update: API_StatusUpdate | ((state: API_StatusState) => API_StatusUpdate) + ) => Promise; /** * Updates the filtering of the index. * @@ -581,13 +584,27 @@ export const init: ModuleFn = ({ }, /* EXPERIMENTAL APIs */ - experimental_updateStatus: async (id, update) => { + experimental_updateStatus: async (id, input) => { const { status, internal_index: index } = store.getState(); const newStatus = { ...status }; + const update = typeof input === 'function' ? input(status) : input; + + if (Object.keys(update).length === 0) { + return; + } + Object.entries(update).forEach(([storyId, value]) => { newStatus[storyId] = { ...(newStatus[storyId] || {}) }; - newStatus[storyId][id] = value; + if (value === null) { + delete newStatus[storyId][id]; + } else { + newStatus[storyId][id] = value; + } + + if (Object.keys(newStatus[storyId]).length === 0) { + delete newStatus[storyId]; + } }); await store.setState({ status: newStatus }, { persistence: 'session' }); diff --git a/code/lib/manager-api/src/tests/stories.test.ts b/code/lib/manager-api/src/tests/stories.test.ts index 0ee0c9be4ebb..a93cd1df9a99 100644 --- a/code/lib/manager-api/src/tests/stories.test.ts +++ b/code/lib/manager-api/src/tests/stories.test.ts @@ -1273,6 +1273,90 @@ describe('stories API', () => { } `); }); + it('delete when value is null', async () => { + const moduleArgs = createMockModuleArgs({}); + const { api } = initStories(moduleArgs as unknown as ModuleArgs); + const { store } = moduleArgs; + + await api.setIndex({ v: 4, entries: mockEntries }); + + await expect( + api.experimental_updateStatus('a-addon-id', { + 'a-story-id': { + status: 'pending', + title: 'an addon title', + description: 'an addon description', + }, + 'another-story-id': { status: 'success', title: 'a addon title', description: '' }, + }) + ).resolves.not.toThrow(); + + // do a second update, this time with null + await expect( + api.experimental_updateStatus('a-addon-id', { + 'a-story-id': null, + 'another-story-id': { status: 'success', title: 'a addon title', description: '' }, + }) + ).resolves.not.toThrow(); + + expect(store.getState().status).toMatchInlineSnapshot(` + Object { + "another-story-id": Object { + "a-addon-id": Object { + "description": "", + "status": "success", + "title": "a addon title", + }, + }, + } + `); + }); + it('updates with a function', async () => { + const moduleArgs = createMockModuleArgs({}); + const { api } = initStories(moduleArgs as unknown as ModuleArgs); + const { store } = moduleArgs; + + await api.setIndex({ v: 4, entries: mockEntries }); + + // setup initial state + await expect( + api.experimental_updateStatus('a-addon-id', () => ({ + 'a-story-id': { + status: 'pending', + title: 'an addon title', + description: 'an addon description', + }, + 'another-story-id': { status: 'success', title: 'a addon title', description: '' }, + })) + ).resolves.not.toThrow(); + + // use existing state in function + await expect( + api.experimental_updateStatus('a-addon-id', (current) => { + return Object.fromEntries( + Object.entries(current).map(([k, v]) => [k, { ...v['a-addon-id'], status: 'success' }]) + ); + }) + ).resolves.not.toThrow(); + expect(store.getState().status).toMatchInlineSnapshot(` + Object { + "a-story-id": Object { + "a-addon-id": Object { + "description": "an addon description", + "status": "success", + "title": "an addon title", + }, + }, + "another-story-id": Object { + "a-addon-id": Object { + "description": "", + "status": "success", + "title": "a addon title", + }, + }, + } + `); + }); }); describe('experimental_setFilter', () => { it('is included in the initial state', async () => { diff --git a/code/lib/types/src/modules/api-stories.ts b/code/lib/types/src/modules/api-stories.ts index 3df42dd812ec..45acc19e35c7 100644 --- a/code/lib/types/src/modules/api-stories.ts +++ b/code/lib/types/src/modules/api-stories.ts @@ -186,5 +186,5 @@ export type API_StatusState = Record>; export type API_StatusUpdate = Record; export type API_FilterFunction = ( - item: API_PreparedIndexEntry & { status: Record } + item: API_PreparedIndexEntry & { status: Record } ) => boolean; diff --git a/code/package.json b/code/package.json index 12a9d0ba65b6..ec2d374e76fa 100644 --- a/code/package.json +++ b/code/package.json @@ -327,5 +327,6 @@ "Dependency Upgrades" ] ] - } + }, + "deferredNextVersion": "7.4.2" } diff --git a/code/ui/components/src/components/Button/Button.tsx b/code/ui/components/src/components/Button/Button.tsx index f39414bf4345..72598c214ec7 100644 --- a/code/ui/components/src/components/Button/Button.tsx +++ b/code/ui/components/src/components/Button/Button.tsx @@ -167,7 +167,7 @@ const ButtonWrapper = styled.button<{ boxShadow: `${color} 0 0 0 1px inset`, color, - 'svg path': { + 'svg path:not([fill])': { fill: color, }, @@ -205,7 +205,7 @@ const ButtonWrapper = styled.button<{ boxShadow: `${color} 0 0 0 1px inset`, color, - 'svg path': { + 'svg path:not([fill])': { fill: color, }, diff --git a/code/ui/components/src/components/tooltip/ListItem.stories.tsx b/code/ui/components/src/components/tooltip/ListItem.stories.tsx index 38344eb63356..8ffeaf322516 100644 --- a/code/ui/components/src/components/tooltip/ListItem.stories.tsx +++ b/code/ui/components/src/components/tooltip/ListItem.stories.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { Fragment } from 'react'; import ListItem from './ListItem'; import { Icons } from '../icon/icon'; @@ -51,6 +51,93 @@ export const ActiveIcon = { right: , }, }; +export const ActiveIconLeft = { + args: { + title: 'Active icon', + active: true, + left: , + }, +}; +export const ActiveIconLeftColored = { + args: { + title: 'Active icon', + active: true, + left: ( + + + + + + + + + + + + + + + + + + + + + + + + + + + ), + }, +}; export const WPositions = { args: { diff --git a/code/ui/components/src/components/tooltip/ListItem.tsx b/code/ui/components/src/components/tooltip/ListItem.tsx index 96c5fa3f58a8..38b6488faa21 100644 --- a/code/ui/components/src/components/tooltip/ListItem.tsx +++ b/code/ui/components/src/components/tooltip/ListItem.tsx @@ -107,7 +107,7 @@ const Left = styled.span( '& svg': { opacity: 1, }, - '& svg path': { + '& svg path:not([fill])': { fill: theme.color.secondary, }, } diff --git a/code/ui/components/src/components/tooltip/WithTooltip.tsx b/code/ui/components/src/components/tooltip/WithTooltip.tsx index 058d7140c2a1..bece4bc64278 100644 --- a/code/ui/components/src/components/tooltip/WithTooltip.tsx +++ b/code/ui/components/src/components/tooltip/WithTooltip.tsx @@ -1,4 +1,4 @@ -import type { FC, ReactNode } from 'react'; +import type { ComponentProps, FC, ReactNode } from 'react'; import React, { useCallback, useState, useEffect } from 'react'; import ReactDOM from 'react-dom'; import { styled } from '@storybook/theming'; @@ -28,6 +28,7 @@ interface WithHideFn { export interface WithTooltipPureProps extends Omit, + Omit, 'trigger'>, PopperOptions { svg?: boolean; withArrows?: boolean; @@ -129,7 +130,7 @@ const WithTooltipPure: FC = ({ return ( <> - + {children} {isVisible && ReactDOM.createPortal(tooltipComponent, document.body)} diff --git a/code/ui/components/src/components/typography/link/link.tsx b/code/ui/components/src/components/typography/link/link.tsx index abd33bad4f48..193fca15e976 100644 --- a/code/ui/components/src/components/typography/link/link.tsx +++ b/code/ui/components/src/components/typography/link/link.tsx @@ -73,13 +73,13 @@ const A = styled.a( '&:hover, &:focus': { cursor: 'pointer', color: darken(0.07, theme.color.secondary), - 'svg path': { + 'svg path:not([fill])': { fill: darken(0.07, theme.color.secondary), }, }, '&:active': { color: darken(0.1, theme.color.secondary), - 'svg path': { + 'svg path:not([fill])': { fill: darken(0.1, theme.color.secondary), }, }, @@ -110,20 +110,20 @@ const A = styled.a( return colors ? { color: colors[0], - 'svg path': { + 'svg path:not([fill])': { fill: colors[0], }, '&:hover': { color: colors[1], - 'svg path': { + 'svg path:not([fill])': { fill: colors[1], }, }, '&:active': { color: colors[2], - 'svg path': { + 'svg path:not([fill])': { fill: colors[2], }, }, @@ -145,20 +145,20 @@ const A = styled.a( inverse ? { color: theme.color.lightest, - 'svg path': { + ':not([fill])': { fill: theme.color.lightest, }, '&:hover': { color: theme.color.lighter, - 'svg path': { + 'svg path:not([fill])': { fill: theme.color.lighter, }, }, '&:active': { color: theme.color.light, - 'svg path': { + 'svg path:not([fill])': { fill: theme.color.light, }, }, diff --git a/code/ui/manager/src/components/sidebar/SearchResults.tsx b/code/ui/manager/src/components/sidebar/SearchResults.tsx index 8cca81f84cb1..2ce58aba337b 100644 --- a/code/ui/manager/src/components/sidebar/SearchResults.tsx +++ b/code/ui/manager/src/components/sidebar/SearchResults.tsx @@ -172,14 +172,12 @@ const Result: FC< node = ; } - const [i, iconColor] = item.status ? statusMapping[item.status] : []; + const [i] = item.status ? statusMapping[item.status] : []; return ( {node} - {item.status ? ( - - ) : null} + {item.status ? i : null} ); }); diff --git a/code/ui/manager/src/components/sidebar/Tree.tsx b/code/ui/manager/src/components/sidebar/Tree.tsx index 8d5ac50484ea..dadff45d9988 100644 --- a/code/ui/manager/src/components/sidebar/Tree.tsx +++ b/code/ui/manager/src/components/sidebar/Tree.tsx @@ -37,44 +37,47 @@ import { } from '../../utils/tree'; import { statusMapping, getHighestStatus, getGroupStatus } from '../../utils/status'; -export const Action = styled.button(({ theme }) => ({ - display: 'inline-flex', - alignItems: 'center', - justifyContent: 'center', - width: 20, - height: 20, - margin: 0, - marginLeft: 'auto', - padding: 0, - outline: 0, - lineHeight: 'normal', - background: 'none', - border: `1px solid transparent`, - borderRadius: '100%', - cursor: 'pointer', - transition: 'all 150ms ease-out', - color: - theme.base === 'light' - ? transparentize(0.3, theme.color.defaultText) - : transparentize(0.6, theme.color.defaultText), - - '&:hover': { - color: theme.color.secondary, - }, - '&:focus': { - color: theme.color.secondary, - borderColor: theme.color.secondary, +export const Action = styled.button<{ height?: number; width?: number }>( + ({ theme, height, width }) => ({ + display: 'inline-flex', + alignItems: 'center', + justifyContent: 'center', + width: width || 20, + height: height || 20, + boxSizing: 'border-box', + margin: 0, + marginLeft: 'auto', + padding: 0, + outline: 0, + lineHeight: 'normal', + background: 'none', + border: `1px solid transparent`, + borderRadius: '100%', + cursor: 'pointer', + transition: 'all 150ms ease-out', + color: + theme.base === 'light' + ? transparentize(0.3, theme.color.defaultText) + : transparentize(0.6, theme.color.defaultText), + + '&:hover': { + color: theme.color.secondary, + }, + '&:focus': { + color: theme.color.secondary, + borderColor: theme.color.secondary, - '&:not(:focus-visible)': { - borderColor: 'transparent', + '&:not(:focus-visible)': { + borderColor: 'transparent', + }, }, - }, - svg: { - width: 10, - height: 10, - }, -})); + svg: { + width: 10, + height: 10, + }, + }) +); const CollapseButton = styled.button(({ theme }) => ({ // Reset button @@ -207,7 +210,7 @@ const Node = React.memo(function Node({ const LeafNode = item.type === 'docs' ? DocumentNode : StoryNode; const statusValue = getHighestStatus(Object.values(status || {}).map((s) => s.status)); - const [icon, iconColor, textColor] = statusMapping[statusValue]; + const [icon, textColor] = statusMapping[statusValue]; return ( (function Node({ {icon ? ( ( ({ id: k, title: v.title, description: v.description, - right: ( - - ), + right: statusMapping[v.status][0], }))} /> )} closeOnOutsideClick > - - + + {icon} ) : null} @@ -530,7 +529,7 @@ export const Tree = React.memo<{ } const isDisplayed = !item.parent || ancestry[itemId].every((a: string) => expanded[a]); - const color = groupStatus[itemId] ? statusMapping[groupStatus[itemId]][2] : null; + const color = groupStatus[itemId] ? statusMapping[groupStatus[itemId]][1] : null; return ( ({ + // specificity hack + animation: `${animation.glow} 1.5s ease-in-out infinite`, + color: base === 'light' ? color.mediumdark : color.darker, +})); + export const statusPriority: API_StatusValue[] = ['unknown', 'pending', 'success', 'warn', 'error']; -export const statusMapping: Record< - API_StatusValue, - [ComponentProps['icon'] | null, string | null, string | null] -> = { - unknown: [null, null, null], - pending: ['watch', 'currentColor', 'currentColor'], - success: ['circle', 'green', 'currentColor'], - warn: ['circle', 'orange', '#A15C20'], - error: ['circle', 'red', 'brown'], +export const statusMapping: Record = { + unknown: [null, null], + pending: [, 'currentColor'], + success: [, 'currentColor'], + warn: [, '#A15C20'], + error: [, 'brown'], }; export const getHighestStatus = (statuses: API_StatusValue[]): API_StatusValue => { diff --git a/docs/versions/latest.json b/docs/versions/latest.json index af61adb7efb2..ff379ea5b978 100644 --- a/docs/versions/latest.json +++ b/docs/versions/latest.json @@ -1 +1 @@ -{"version":"7.4.1","info":{"plain":"- CLI: Add uncaughtException handler - [#24018](https://github.com/storybookjs/storybook/pull/24018), thanks [@yannbf](https://github.com/yannbf)!\n- CLI: Fix packageManager handling in `sb add` - [#24079](https://github.com/storybookjs/storybook/pull/24079), thanks [@Integrayshaun](https://github.com/Integrayshaun)!\n- Core: Add CJS entrypoints to errors in core events - [#24038](https://github.com/storybookjs/storybook/pull/24038), thanks [@yannbf](https://github.com/yannbf)!\n- Docs: Fix TOC import - [#24047](https://github.com/storybookjs/storybook/pull/24047), thanks [@shilman](https://github.com/shilman)!\n- Telemetry: Filter addon options to protect sensitive info - [#24000](https://github.com/storybookjs/storybook/pull/24000), thanks [@shilman](https://github.com/shilman)!\n- Types: Remove `@types/react` dep from `@storybook/types` - [#24042](https://github.com/storybookjs/storybook/pull/24042), thanks [@JReinhold](https://github.com/JReinhold)!\n- Vue3: Remove console.log in sourceDecorator - [#24062](https://github.com/storybookjs/storybook/pull/24062), thanks [@oruman](https://github.com/oruman)!"}} +{"version":"7.4.2","info":{"plain":"- Addon API: Improve the updateStatus API - [#24007](https://github.com/storybookjs/storybook/pull/24007), thanks [@ndelangen](https://github.com/ndelangen)!\n- Nextjs: Migrate from config to previewAnnotations - [#24178](https://github.com/storybookjs/storybook/pull/24178), thanks [@yannbf](https://github.com/yannbf)!\n- UI: Fix SVG override fill when path has a fill attribute - [#24156](https://github.com/storybookjs/storybook/pull/24156), thanks [@ndelangen](https://github.com/ndelangen)!\n- UI: Improve look and feel of status UI in sidebar - [#24099](https://github.com/storybookjs/storybook/pull/24099), thanks [@ndelangen](https://github.com/ndelangen)!"}} diff --git a/docs/versions/next.json b/docs/versions/next.json index 739652e36af9..5c73bf231c87 100644 --- a/docs/versions/next.json +++ b/docs/versions/next.json @@ -1 +1 @@ -{"version":"7.5.0-alpha.1","info":{"plain":"- Core: Add CJS entrypoints to errors in core events - [#24038](https://github.com/storybookjs/storybook/pull/24038), thanks [@yannbf](https://github.com/yannbf)!"}} +{"version":"7.5.0-alpha.2","info":{"plain":"- Angular: Categorize legacy build options error - [#24014](https://github.com/storybookjs/storybook/pull/24014), thanks [@yannbf](https://github.com/yannbf)!\n- Builder-Webpack5: Categorize builder error - [#24031](https://github.com/storybookjs/storybook/pull/24031), thanks [@yannbf](https://github.com/yannbf)!\n- CI: Inform the user how to dedupe and strip color from info command - [#24087](https://github.com/storybookjs/storybook/pull/24087), thanks [@kasperpeulen](https://github.com/kasperpeulen)!\n- CLI: Fix packageManager handling in `sb add` - [#24079](https://github.com/storybookjs/storybook/pull/24079), thanks [@Integrayshaun](https://github.com/Integrayshaun)!\n- CLI: Improve sanitization logic in crash reports - [#24028](https://github.com/storybookjs/storybook/pull/24028), thanks [@yannbf](https://github.com/yannbf)!\n- Maintenance: Add more context to explanation in core-events errors - [#24063](https://github.com/storybookjs/storybook/pull/24063), thanks [@yannbf](https://github.com/yannbf)!\n- Monorepo: Fix `svelte-vite` detection - [#24085](https://github.com/storybookjs/storybook/pull/24085), thanks [@legnaleurc](https://github.com/legnaleurc)!\n- NextJS: Fix Image Context reuse (ensure singleton by externalizing it) - [#23881](https://github.com/storybookjs/storybook/pull/23881), thanks [@martinnabhan](https://github.com/martinnabhan)!\n- Source-loader: Fix property key validation - [#24068](https://github.com/storybookjs/storybook/pull/24068), thanks [@MrZillaGold](https://github.com/MrZillaGold)!\n- Svelte: Fix generated properties on Svelte event handler - [#24020](https://github.com/storybookjs/storybook/pull/24020), thanks [@j3rem1e](https://github.com/j3rem1e)!\n- Telemetry: Add platform info to telemetry event - [#24081](https://github.com/storybookjs/storybook/pull/24081), thanks [@yannbf](https://github.com/yannbf)!\n- UI: Fix target id in searchfield label - [#23464](https://github.com/storybookjs/storybook/pull/23464), thanks [@plumpNation](https://github.com/plumpNation)!\n- Vue3: Remove console.log in sourceDecorator - [#24062](https://github.com/storybookjs/storybook/pull/24062), thanks [@oruman](https://github.com/oruman)!"}}