storybook-addon-vis 3.1.0 → 3.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/manager.js CHANGED
@@ -6,7 +6,11 @@ import { styled } from "storybook/theming";
6
6
  import { SNAPSHOT_ROOT_DIR } from "vitest-plugin-vis/client-api";
7
7
 
8
8
  //#region src/components/vis_panel.tsx
9
- const VisPanel = memo(function VisPanel$1({ active, snapshotResults = [], onRefresh }) {
9
+ const VisPanel = memo(function VisPanel$1({ active, getSnapshotResults, onRefresh }) {
10
+ const [snapshotResults, setSnapshotResults] = useState([]);
11
+ useEffect(() => {
12
+ getSnapshotResults().then(setSnapshotResults);
13
+ }, [getSnapshotResults]);
10
14
  const groupedResults = snapshotResults.reduce((acc, result) => {
11
15
  const key = `(${result.snapshotRootDir.slice(SNAPSHOT_ROOT_DIR.length + 1)}) ${result.fileName}`;
12
16
  acc[key] = [...acc[key] ?? [], result];
@@ -16,7 +20,10 @@ const VisPanel = memo(function VisPanel$1({ active, snapshotResults = [], onRefr
16
20
  key: result.filePath,
17
21
  src: `data:image/png;base64,${result.base64}`,
18
22
  alt: result.fileName
19
- }))) : /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(SnapshotRow, null, /* @__PURE__ */ React.createElement("span", null, key), /* @__PURE__ */ React.createElement(IconButton, { onClick: () => onRefresh() }, /* @__PURE__ */ React.createElement(RefreshIcon, null))), /* @__PURE__ */ React.createElement("img", {
23
+ }))) : /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(SnapshotRow, null, /* @__PURE__ */ React.createElement("span", null, key), /* @__PURE__ */ React.createElement(IconButton, { onClick: () => {
24
+ onRefresh();
25
+ getSnapshotResults().then(setSnapshotResults);
26
+ } }, /* @__PURE__ */ React.createElement(RefreshIcon, null))), /* @__PURE__ */ React.createElement("img", {
20
27
  key: results[0].filePath,
21
28
  src: `data:image/png;base64,${results[0].base64}`,
22
29
  alt: results[0].fileName
@@ -57,23 +64,22 @@ addons.register(NAME, (api) => {
57
64
  title: "Vis",
58
65
  match: ({ tabId, viewMode }) => !tabId && viewMode === "story",
59
66
  render({ active }) {
60
- const [snapshotResults, setSnapshotResults] = useState([]);
67
+ if (!active) return null;
61
68
  const storyData = api.getCurrentStoryData();
62
- useEffect(() => {
63
- const disposes = [api.on(NAME, (event) => {
64
- if (event.name !== storyData.name) return;
65
- if (event.importPath !== storyData.importPath) return;
66
- if (event.type === IMAGE_SNAPSHOT_RESULTS_RESPONSE) setSnapshotResults(event.results);
67
- })];
68
- if (storyData) api.emit(NAME, requestImageSnapshotResults(storyData));
69
- return () => disposes.forEach((dispose) => {
70
- dispose();
69
+ if (!storyData) return null;
70
+ api.emit(NAME, requestImageSnapshotResults(storyData));
71
+ async function getSnapshotResults() {
72
+ return new Promise((resolve) => {
73
+ api.once(NAME, (event) => {
74
+ if (event.name !== storyData.name) return;
75
+ if (event.importPath !== storyData.importPath) return;
76
+ if (event.type === IMAGE_SNAPSHOT_RESULTS_RESPONSE) resolve(event.results);
77
+ });
71
78
  });
72
- }, [storyData]);
73
- if (!active) return null;
79
+ }
74
80
  return /* @__PURE__ */ React.createElement(VisPanel, {
75
81
  active,
76
- snapshotResults,
82
+ getSnapshotResults,
77
83
  onRefresh: () => {
78
84
  if (storyData) api.emit(NAME, requestImageSnapshotResults(storyData));
79
85
  }
@@ -1 +1 @@
1
- {"version":3,"file":"manager.js","names":["VisPanel"],"sources":["../src/components/vis_panel.tsx","../src/shared/contants.ts","../src/shared/events.ts","../src/manager.tsx"],"sourcesContent":["import { RefreshIcon } from '@storybook/icons'\nimport React, { memo } from 'react'\nimport { AddonPanel, IconButton, Placeholder, ScrollArea } from 'storybook/internal/components'\nimport { styled } from 'storybook/theming'\nimport { SNAPSHOT_ROOT_DIR } from 'vitest-plugin-vis/client-api'\nimport type { ImageSnapshotResults } from '../shared/events.ts'\n\ninterface PanelProps {\n\tactive: boolean\n\tsnapshotResults: ImageSnapshotResults[]\n\tonRefresh: () => void\n}\n\nexport const VisPanel = memo(function VisPanel({ active, snapshotResults = [], onRefresh }: PanelProps) {\n\tconst groupedResults = snapshotResults.reduce(\n\t\t(acc, result) => {\n\t\t\tconst suiteName = result.snapshotRootDir.slice(SNAPSHOT_ROOT_DIR.length + 1)\n\t\t\tconst key = `(${suiteName}) ${result.fileName}`\n\t\t\tacc[key] = [...(acc[key] ?? []), result]\n\t\t\treturn acc\n\t\t},\n\t\t{} as Record<string, ImageSnapshotResults[]>,\n\t)\n\treturn (\n\t\t<AddonPanel active={active}>\n\t\t\t{snapshotResults.length > 0 ? (\n\t\t\t\t<ScrollArea vertical>\n\t\t\t\t\t{Object.entries(groupedResults).map(([key, results]) => (\n\t\t\t\t\t\t<div key={key}>\n\t\t\t\t\t\t\t{results.some((result) => result.type === 'diff') ? (\n\t\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t\t<SnapshotRow failed>{key}</SnapshotRow>\n\t\t\t\t\t\t\t\t\t{results.map((result) => (\n\t\t\t\t\t\t\t\t\t\t<img key={result.filePath} src={`data:image/png;base64,${result.base64}`} alt={result.fileName} />\n\t\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t\t<SnapshotRow>\n\t\t\t\t\t\t\t\t\t\t<span>{key}</span>\n\t\t\t\t\t\t\t\t\t\t<IconButton onClick={() => onRefresh()}>\n\t\t\t\t\t\t\t\t\t\t\t<RefreshIcon />\n\t\t\t\t\t\t\t\t\t\t</IconButton>\n\t\t\t\t\t\t\t\t\t</SnapshotRow>\n\t\t\t\t\t\t\t\t\t<img\n\t\t\t\t\t\t\t\t\t\tkey={results[0].filePath}\n\t\t\t\t\t\t\t\t\t\tsrc={`data:image/png;base64,${results[0].base64}`}\n\t\t\t\t\t\t\t\t\t\talt={results[0].fileName}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t))}\n\t\t\t\t</ScrollArea>\n\t\t\t) : (\n\t\t\t\t<Placeholder>There is no snapshots for this story</Placeholder>\n\t\t\t)}\n\t\t</AddonPanel>\n\t)\n})\n\nconst SnapshotRow = styled.div<{ failed?: boolean }>(({ theme, failed }) => ({\n\tdisplay: 'flex',\n\talignItems: 'center',\n\tgap: '0.5rem',\n\tpaddingBlock: '0.5rem',\n\tpaddingInline: '0.2rem',\n\tbackgroundColor: failed ? theme.background.negative : theme.background.positive,\n\tcolor: theme.barTextColor,\n}))\n","export const NAME = 'storybook-addon-vis'\nexport const VIS_PANEL_ID = `${NAME}/vis_panel`\n","export type VisEvent = ImageSnapshotResultsRequest | ImageSnapshotResultsResponse\n\nexport const IMAGE_SNAPSHOT_RESULTS_REQUEST = 'IMAGE_SNAPSHOT_RESULTS_REQUEST'\nexport const IMAGE_SNAPSHOT_RESULTS_RESPONSE = 'IMAGE_SNAPSHOT_RESULTS_RESPONSE'\n\nexport function requestImageSnapshotResults({ name, importPath }: { name: string; importPath: string }) {\n\treturn {\n\t\ttype: IMAGE_SNAPSHOT_RESULTS_REQUEST,\n\t\tname,\n\t\timportPath,\n\t}\n}\n\nexport type ImageSnapshotResultsRequest = {\n\ttype: typeof IMAGE_SNAPSHOT_RESULTS_REQUEST\n\tname: string\n\timportPath: string\n}\n\nexport type ImageSnapshotResultsResponse = {\n\ttype: typeof IMAGE_SNAPSHOT_RESULTS_RESPONSE\n\tname: string\n\timportPath: string\n\tresults: ImageSnapshotResults[]\n}\n\nexport type ImageSnapshotResults = {\n\tfilePath: string\n\tfileName: string\n\tsnapshotRootDir: string\n\ttype: 'baseline' | 'diff' | 'result'\n\tbase64: string\n}\n","import React, { useEffect, useState } from 'react'\nimport { addons, types } from 'storybook/manager-api'\nimport { VisPanel } from './components/vis_panel.tsx'\nimport { NAME, VIS_PANEL_ID } from './shared/contants.ts'\nimport {\n\tIMAGE_SNAPSHOT_RESULTS_RESPONSE,\n\trequestImageSnapshotResults,\n\ttype ImageSnapshotResults,\n\ttype VisEvent,\n} from './shared/events.ts'\n\n// Register the addon\naddons.register(NAME, (api) => {\n\t// Register the tool\n\tif ((globalThis as any).CONFIG_TYPE === 'DEVELOPMENT') {\n\t\taddons.add(VIS_PANEL_ID, {\n\t\t\ttype: types.PANEL,\n\t\t\ttitle: 'Vis',\n\t\t\tmatch: ({ tabId, viewMode }) => !tabId && viewMode === 'story',\n\t\t\trender({ active }) {\n\t\t\t\tconst [snapshotResults, setSnapshotResults] = useState<ImageSnapshotResults[]>([])\n\n\t\t\t\tconst storyData = api.getCurrentStoryData()\n\n\t\t\t\tuseEffect(() => {\n\t\t\t\t\tconst disposes = [\n\t\t\t\t\t\tapi.on(NAME, (event: VisEvent) => {\n\t\t\t\t\t\t\tif (event.name !== storyData.name) return\n\t\t\t\t\t\t\tif (event.importPath !== storyData.importPath) return\n\n\t\t\t\t\t\t\tif (event.type === IMAGE_SNAPSHOT_RESULTS_RESPONSE) {\n\t\t\t\t\t\t\t\tsetSnapshotResults(event.results)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}),\n\t\t\t\t\t\t// api.on('UNIVERSAL_STORE:storybook/test', (event) => {\n\t\t\t\t\t\t// \tconsole.log('storybook/test', event)\n\t\t\t\t\t\t// }),\n\t\t\t\t\t]\n\t\t\t\t\tif (storyData) api.emit(NAME, requestImageSnapshotResults(storyData))\n\t\t\t\t\treturn () =>\n\t\t\t\t\t\tdisposes.forEach((dispose) => {\n\t\t\t\t\t\t\tdispose()\n\t\t\t\t\t\t})\n\t\t\t\t}, [storyData])\n\n\t\t\t\tif (!active) return null\n\n\t\t\t\treturn (\n\t\t\t\t\t<VisPanel\n\t\t\t\t\t\tactive={active}\n\t\t\t\t\t\tsnapshotResults={snapshotResults}\n\t\t\t\t\t\tonRefresh={() => {\n\t\t\t\t\t\t\tif (storyData) api.emit(NAME, requestImageSnapshotResults(storyData))\n\t\t\t\t\t\t}}\n\t\t\t\t\t/>\n\t\t\t\t)\n\t\t\t},\n\t\t})\n\t}\n})\n"],"mappings":";;;;;;;;AAaA,MAAa,WAAW,KAAK,SAASA,WAAS,EAAE,QAAQ,kBAAkB,EAAE,EAAE,aAAyB;CACvG,MAAM,iBAAiB,gBAAgB,QACrC,KAAK,WAAW;EAEhB,MAAM,MAAM,IADM,OAAO,gBAAgB,MAAM,kBAAkB,SAAS,EAAE,CAClD,IAAI,OAAO;AACrC,MAAI,OAAO,CAAC,GAAI,IAAI,QAAQ,EAAE,EAAG,OAAO;AACxC,SAAO;IAER,EAAE,CACF;AACD,QACC,oCAAC,cAAmB,UAClB,gBAAgB,SAAS,IACzB,oCAAC,cAAW,kBACV,OAAO,QAAQ,eAAe,CAAC,KAAK,CAAC,KAAK,aAC1C,oCAAC,SAAS,OACR,QAAQ,MAAM,WAAW,OAAO,SAAS,OAAO,GAChD,0DACC,oCAAC,eAAY,gBAAQ,IAAkB,EACtC,QAAQ,KAAK,WACb,oCAAC;EAAI,KAAK,OAAO;EAAU,KAAK,yBAAyB,OAAO;EAAU,KAAK,OAAO;GAAY,CACjG,CACA,GAEH,0DACC,oCAAC,mBACA,oCAAC,cAAM,IAAW,EAClB,oCAAC,cAAW,eAAe,WAAW,IACrC,oCAAC,kBAAc,CACH,CACA,EACd,oCAAC;EACA,KAAK,QAAQ,GAAG;EAChB,KAAK,yBAAyB,QAAQ,GAAG;EACzC,KAAK,QAAQ,GAAG;GACf,CACA,CAEC,CACL,CACU,GAEb,oCAAC,mBAAY,uCAAkD,CAEpD;EAEb;AAEF,MAAM,cAAc,OAAO,KAA2B,EAAE,OAAO,cAAc;CAC5E,SAAS;CACT,YAAY;CACZ,KAAK;CACL,cAAc;CACd,eAAe;CACf,iBAAiB,SAAS,MAAM,WAAW,WAAW,MAAM,WAAW;CACvE,OAAO,MAAM;CACb,EAAE;;;;ACrEH,MAAa,OAAO;AACpB,MAAa,eAAe,GAAG,KAAK;;;;ACCpC,MAAa,iCAAiC;AAC9C,MAAa,kCAAkC;AAE/C,SAAgB,4BAA4B,EAAE,MAAM,cAAoD;AACvG,QAAO;EACN,MAAM;EACN;EACA;EACA;;;;;ACEF,OAAO,SAAS,OAAO,QAAQ;AAE9B,KAAK,WAAmB,gBAAgB,cACvC,QAAO,IAAI,cAAc;EACxB,MAAM,MAAM;EACZ,OAAO;EACP,QAAQ,EAAE,OAAO,eAAe,CAAC,SAAS,aAAa;EACvD,OAAO,EAAE,UAAU;GAClB,MAAM,CAAC,iBAAiB,sBAAsB,SAAiC,EAAE,CAAC;GAElF,MAAM,YAAY,IAAI,qBAAqB;AAE3C,mBAAgB;IACf,MAAM,WAAW,CAChB,IAAI,GAAG,OAAO,UAAoB;AACjC,SAAI,MAAM,SAAS,UAAU,KAAM;AACnC,SAAI,MAAM,eAAe,UAAU,WAAY;AAE/C,SAAI,MAAM,SAAS,gCAClB,oBAAmB,MAAM,QAAQ;MAEjC,CAIF;AACD,QAAI,UAAW,KAAI,KAAK,MAAM,4BAA4B,UAAU,CAAC;AACrE,iBACC,SAAS,SAAS,YAAY;AAC7B,cAAS;MACR;MACD,CAAC,UAAU,CAAC;AAEf,OAAI,CAAC,OAAQ,QAAO;AAEpB,UACC,oCAAC;IACQ;IACS;IACjB,iBAAiB;AAChB,SAAI,UAAW,KAAI,KAAK,MAAM,4BAA4B,UAAU,CAAC;;KAErE;;EAGJ,CAAC;EAEF"}
1
+ {"version":3,"file":"manager.js","names":["VisPanel"],"sources":["../src/components/vis_panel.tsx","../src/shared/contants.ts","../src/shared/events.ts","../src/manager.tsx"],"sourcesContent":["import { RefreshIcon } from '@storybook/icons'\nimport React, { memo, useEffect, useState } from 'react'\nimport { AddonPanel, IconButton, Placeholder, ScrollArea } from 'storybook/internal/components'\nimport { styled } from 'storybook/theming'\nimport { SNAPSHOT_ROOT_DIR } from 'vitest-plugin-vis/client-api'\nimport type { ImageSnapshotResults } from '../shared/events.ts'\n\ninterface PanelProps {\n\tactive: boolean\n\tgetSnapshotResults: () => Promise<ImageSnapshotResults[]>\n\tonRefresh: () => void\n}\n\nexport const VisPanel = memo(function VisPanel({ active, getSnapshotResults, onRefresh }: PanelProps) {\n\tconst [snapshotResults, setSnapshotResults] = useState<ImageSnapshotResults[]>([])\n\n\tuseEffect(() => {\n\t\tgetSnapshotResults().then(setSnapshotResults)\n\t}, [getSnapshotResults])\n\n\tconst groupedResults = snapshotResults.reduce(\n\t\t(acc, result) => {\n\t\t\tconst suiteName = result.snapshotRootDir.slice(SNAPSHOT_ROOT_DIR.length + 1)\n\t\t\tconst key = `(${suiteName}) ${result.fileName}`\n\t\t\tacc[key] = [...(acc[key] ?? []), result]\n\t\t\treturn acc\n\t\t},\n\t\t{} as Record<string, ImageSnapshotResults[]>,\n\t)\n\treturn (\n\t\t<AddonPanel active={active}>\n\t\t\t{snapshotResults.length > 0 ? (\n\t\t\t\t<ScrollArea vertical>\n\t\t\t\t\t{Object.entries(groupedResults).map(([key, results]) => (\n\t\t\t\t\t\t<div key={key}>\n\t\t\t\t\t\t\t{results.some((result) => result.type === 'diff') ? (\n\t\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t\t<SnapshotRow failed>{key}</SnapshotRow>\n\t\t\t\t\t\t\t\t\t{results.map((result) => (\n\t\t\t\t\t\t\t\t\t\t<img key={result.filePath} src={`data:image/png;base64,${result.base64}`} alt={result.fileName} />\n\t\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t\t<SnapshotRow>\n\t\t\t\t\t\t\t\t\t\t<span>{key}</span>\n\t\t\t\t\t\t\t\t\t\t<IconButton\n\t\t\t\t\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\t\t\t\t\tonRefresh()\n\t\t\t\t\t\t\t\t\t\t\t\tgetSnapshotResults().then(setSnapshotResults)\n\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t<RefreshIcon />\n\t\t\t\t\t\t\t\t\t\t</IconButton>\n\t\t\t\t\t\t\t\t\t</SnapshotRow>\n\t\t\t\t\t\t\t\t\t<img\n\t\t\t\t\t\t\t\t\t\tkey={results[0].filePath}\n\t\t\t\t\t\t\t\t\t\tsrc={`data:image/png;base64,${results[0].base64}`}\n\t\t\t\t\t\t\t\t\t\talt={results[0].fileName}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t))}\n\t\t\t\t</ScrollArea>\n\t\t\t) : (\n\t\t\t\t<Placeholder>There is no snapshots for this story</Placeholder>\n\t\t\t)}\n\t\t</AddonPanel>\n\t)\n})\n\nconst SnapshotRow = styled.div<{ failed?: boolean }>(({ theme, failed }) => ({\n\tdisplay: 'flex',\n\talignItems: 'center',\n\tgap: '0.5rem',\n\tpaddingBlock: '0.5rem',\n\tpaddingInline: '0.2rem',\n\tbackgroundColor: failed ? theme.background.negative : theme.background.positive,\n\tcolor: theme.barTextColor,\n}))\n","export const NAME = 'storybook-addon-vis'\nexport const VIS_PANEL_ID = `${NAME}/vis_panel`\n","export type VisEvent = ImageSnapshotResultsRequest | ImageSnapshotResultsResponse\n\nexport const IMAGE_SNAPSHOT_RESULTS_REQUEST = 'IMAGE_SNAPSHOT_RESULTS_REQUEST'\nexport const IMAGE_SNAPSHOT_RESULTS_RESPONSE = 'IMAGE_SNAPSHOT_RESULTS_RESPONSE'\n\nexport function requestImageSnapshotResults({ name, importPath }: { name: string; importPath: string }) {\n\treturn {\n\t\ttype: IMAGE_SNAPSHOT_RESULTS_REQUEST,\n\t\tname,\n\t\timportPath,\n\t}\n}\n\nexport type ImageSnapshotResultsRequest = {\n\ttype: typeof IMAGE_SNAPSHOT_RESULTS_REQUEST\n\tname: string\n\timportPath: string\n}\n\nexport type ImageSnapshotResultsResponse = {\n\ttype: typeof IMAGE_SNAPSHOT_RESULTS_RESPONSE\n\tname: string\n\timportPath: string\n\tresults: ImageSnapshotResults[]\n}\n\nexport type ImageSnapshotResults = {\n\tfilePath: string\n\tfileName: string\n\tsnapshotRootDir: string\n\ttype: 'baseline' | 'diff' | 'result'\n\tbase64: string\n}\n","import React from 'react'\nimport { addons, types } from 'storybook/manager-api'\nimport { VisPanel } from './components/vis_panel.tsx'\nimport { NAME, VIS_PANEL_ID } from './shared/contants.ts'\nimport {\n\tIMAGE_SNAPSHOT_RESULTS_RESPONSE,\n\trequestImageSnapshotResults,\n\ttype ImageSnapshotResults,\n\ttype VisEvent,\n} from './shared/events.ts'\n\n// Register the addon\naddons.register(NAME, (api) => {\n\t// Register the tool\n\tif ((globalThis as any).CONFIG_TYPE === 'DEVELOPMENT') {\n\t\taddons.add(VIS_PANEL_ID, {\n\t\t\ttype: types.PANEL,\n\t\t\ttitle: 'Vis',\n\t\t\tmatch: ({ tabId, viewMode }) => !tabId && viewMode === 'story',\n\t\t\trender({ active }) {\n\t\t\t\tif (!active) return null\n\t\t\t\tconst storyData = api.getCurrentStoryData()\n\t\t\t\tif (!storyData) return null\n\t\t\t\tapi.emit(NAME, requestImageSnapshotResults(storyData))\n\t\t\t\tasync function getSnapshotResults() {\n\t\t\t\t\treturn new Promise<ImageSnapshotResults[]>((resolve) => {\n\t\t\t\t\t\tapi.once(NAME, (event: VisEvent) => {\n\t\t\t\t\t\t\tif (event.name !== storyData.name) return\n\t\t\t\t\t\t\tif (event.importPath !== storyData.importPath) return\n\t\t\t\t\t\t\tif (event.type === IMAGE_SNAPSHOT_RESULTS_RESPONSE) {\n\t\t\t\t\t\t\t\tresolve(event.results)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t})\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t\treturn (\n\t\t\t\t\t<VisPanel\n\t\t\t\t\t\tactive={active}\n\t\t\t\t\t\tgetSnapshotResults={getSnapshotResults}\n\t\t\t\t\t\tonRefresh={() => {\n\t\t\t\t\t\t\tif (storyData) api.emit(NAME, requestImageSnapshotResults(storyData))\n\t\t\t\t\t\t}}\n\t\t\t\t\t/>\n\t\t\t\t)\n\t\t\t},\n\t\t})\n\t}\n})\n"],"mappings":";;;;;;;;AAaA,MAAa,WAAW,KAAK,SAASA,WAAS,EAAE,QAAQ,oBAAoB,aAAyB;CACrG,MAAM,CAAC,iBAAiB,sBAAsB,SAAiC,EAAE,CAAC;AAElF,iBAAgB;AACf,sBAAoB,CAAC,KAAK,mBAAmB;IAC3C,CAAC,mBAAmB,CAAC;CAExB,MAAM,iBAAiB,gBAAgB,QACrC,KAAK,WAAW;EAEhB,MAAM,MAAM,IADM,OAAO,gBAAgB,MAAM,kBAAkB,SAAS,EAAE,CAClD,IAAI,OAAO;AACrC,MAAI,OAAO,CAAC,GAAI,IAAI,QAAQ,EAAE,EAAG,OAAO;AACxC,SAAO;IAER,EAAE,CACF;AACD,QACC,oCAAC,cAAmB,UAClB,gBAAgB,SAAS,IACzB,oCAAC,cAAW,kBACV,OAAO,QAAQ,eAAe,CAAC,KAAK,CAAC,KAAK,aAC1C,oCAAC,SAAS,OACR,QAAQ,MAAM,WAAW,OAAO,SAAS,OAAO,GAChD,0DACC,oCAAC,eAAY,gBAAQ,IAAkB,EACtC,QAAQ,KAAK,WACb,oCAAC;EAAI,KAAK,OAAO;EAAU,KAAK,yBAAyB,OAAO;EAAU,KAAK,OAAO;GAAY,CACjG,CACA,GAEH,0DACC,oCAAC,mBACA,oCAAC,cAAM,IAAW,EAClB,oCAAC,cACA,eAAe;AACd,aAAW;AACX,sBAAoB,CAAC,KAAK,mBAAmB;MAG9C,oCAAC,kBAAc,CACH,CACA,EACd,oCAAC;EACA,KAAK,QAAQ,GAAG;EAChB,KAAK,yBAAyB,QAAQ,GAAG;EACzC,KAAK,QAAQ,GAAG;GACf,CACA,CAEC,CACL,CACU,GAEb,oCAAC,mBAAY,uCAAkD,CAEpD;EAEb;AAEF,MAAM,cAAc,OAAO,KAA2B,EAAE,OAAO,cAAc;CAC5E,SAAS;CACT,YAAY;CACZ,KAAK;CACL,cAAc;CACd,eAAe;CACf,iBAAiB,SAAS,MAAM,WAAW,WAAW,MAAM,WAAW;CACvE,OAAO,MAAM;CACb,EAAE;;;;AChFH,MAAa,OAAO;AACpB,MAAa,eAAe,GAAG,KAAK;;;;ACCpC,MAAa,iCAAiC;AAC9C,MAAa,kCAAkC;AAE/C,SAAgB,4BAA4B,EAAE,MAAM,cAAoD;AACvG,QAAO;EACN,MAAM;EACN;EACA;EACA;;;;;ACEF,OAAO,SAAS,OAAO,QAAQ;AAE9B,KAAK,WAAmB,gBAAgB,cACvC,QAAO,IAAI,cAAc;EACxB,MAAM,MAAM;EACZ,OAAO;EACP,QAAQ,EAAE,OAAO,eAAe,CAAC,SAAS,aAAa;EACvD,OAAO,EAAE,UAAU;AAClB,OAAI,CAAC,OAAQ,QAAO;GACpB,MAAM,YAAY,IAAI,qBAAqB;AAC3C,OAAI,CAAC,UAAW,QAAO;AACvB,OAAI,KAAK,MAAM,4BAA4B,UAAU,CAAC;GACtD,eAAe,qBAAqB;AACnC,WAAO,IAAI,SAAiC,YAAY;AACvD,SAAI,KAAK,OAAO,UAAoB;AACnC,UAAI,MAAM,SAAS,UAAU,KAAM;AACnC,UAAI,MAAM,eAAe,UAAU,WAAY;AAC/C,UAAI,MAAM,SAAS,gCAClB,SAAQ,MAAM,QAAQ;OAEtB;MACD;;AAEH,UACC,oCAAC;IACQ;IACY;IACpB,iBAAiB;AAChB,SAAI,UAAW,KAAI,KAAK,MAAM,4BAA4B,UAAU,CAAC;;KAErE;;EAGJ,CAAC;EAEF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "storybook-addon-vis",
3
- "version": "3.1.0",
3
+ "version": "3.1.2",
4
4
  "description": "Storybook Vitest visual testing addon",
5
5
  "keywords": [
6
6
  "image-snapshot",
@@ -72,9 +72,9 @@
72
72
  "devDependencies": {
73
73
  "@repobuddy/storybook": "^2.1.2",
74
74
  "@storybook-community/storybook-dark-mode": "^7.0.3",
75
- "@storybook/addon-docs": "^10.2.0-alpha.1",
76
- "@storybook/addon-vitest": "^10.2.0-alpha.1",
77
- "@storybook/react-vite": "^10.2.0-alpha.1",
75
+ "@storybook/addon-docs": "^10.1.10",
76
+ "@storybook/addon-vitest": "^10.1.10",
77
+ "@storybook/react-vite": "^10.1.10",
78
78
  "@tailwindcss/cli": "^4.1.7",
79
79
  "@tailwindcss/vite": "^4.1.17",
80
80
  "@testing-library/react": "^16.0.1",
@@ -93,10 +93,10 @@
93
93
  "playwright": "latest",
94
94
  "react": "^19.0.0",
95
95
  "react-dom": "^19.0.0",
96
- "storybook": "^10.2.0-alpha.1",
96
+ "storybook": "^10.1.10",
97
97
  "storybook-addon-tag-badges": "^3.0.2",
98
98
  "tailwindcss": "^4.1.14",
99
- "tsdown": "^0.16.6",
99
+ "tsdown": "^0.18.0",
100
100
  "typescript": "^5.9.3",
101
101
  "vite": "^7.1.12",
102
102
  "vitest": "^4.0.9",
@@ -105,9 +105,9 @@
105
105
  "zx": "^8.0.0"
106
106
  },
107
107
  "peerDependencies": {
108
- "@storybook/addon-vitest": "^10.2.0-alpha.1",
108
+ "@storybook/addon-vitest": "^10.1.10",
109
109
  "@vitest/browser": "^4",
110
- "storybook": "^10.2.0-alpha.1",
110
+ "storybook": "^10.1.10",
111
111
  "vitest": "^4"
112
112
  },
113
113
  "publishConfig": {
@@ -1,5 +1,5 @@
1
1
  import { RefreshIcon } from '@storybook/icons'
2
- import React, { memo } from 'react'
2
+ import React, { memo, useEffect, useState } from 'react'
3
3
  import { AddonPanel, IconButton, Placeholder, ScrollArea } from 'storybook/internal/components'
4
4
  import { styled } from 'storybook/theming'
5
5
  import { SNAPSHOT_ROOT_DIR } from 'vitest-plugin-vis/client-api'
@@ -7,11 +7,17 @@ import type { ImageSnapshotResults } from '../shared/events.ts'
7
7
 
8
8
  interface PanelProps {
9
9
  active: boolean
10
- snapshotResults: ImageSnapshotResults[]
10
+ getSnapshotResults: () => Promise<ImageSnapshotResults[]>
11
11
  onRefresh: () => void
12
12
  }
13
13
 
14
- export const VisPanel = memo(function VisPanel({ active, snapshotResults = [], onRefresh }: PanelProps) {
14
+ export const VisPanel = memo(function VisPanel({ active, getSnapshotResults, onRefresh }: PanelProps) {
15
+ const [snapshotResults, setSnapshotResults] = useState<ImageSnapshotResults[]>([])
16
+
17
+ useEffect(() => {
18
+ getSnapshotResults().then(setSnapshotResults)
19
+ }, [getSnapshotResults])
20
+
15
21
  const groupedResults = snapshotResults.reduce(
16
22
  (acc, result) => {
17
23
  const suiteName = result.snapshotRootDir.slice(SNAPSHOT_ROOT_DIR.length + 1)
@@ -38,7 +44,12 @@ export const VisPanel = memo(function VisPanel({ active, snapshotResults = [], o
38
44
  <>
39
45
  <SnapshotRow>
40
46
  <span>{key}</span>
41
- <IconButton onClick={() => onRefresh()}>
47
+ <IconButton
48
+ onClick={() => {
49
+ onRefresh()
50
+ getSnapshotResults().then(setSnapshotResults)
51
+ }}
52
+ >
42
53
  <RefreshIcon />
43
54
  </IconButton>
44
55
  </SnapshotRow>
package/src/manager.tsx CHANGED
@@ -1,4 +1,4 @@
1
- import React, { useEffect, useState } from 'react'
1
+ import React from 'react'
2
2
  import { addons, types } from 'storybook/manager-api'
3
3
  import { VisPanel } from './components/vis_panel.tsx'
4
4
  import { NAME, VIS_PANEL_ID } from './shared/contants.ts'
@@ -18,37 +18,25 @@ addons.register(NAME, (api) => {
18
18
  title: 'Vis',
19
19
  match: ({ tabId, viewMode }) => !tabId && viewMode === 'story',
20
20
  render({ active }) {
21
- const [snapshotResults, setSnapshotResults] = useState<ImageSnapshotResults[]>([])
22
-
21
+ if (!active) return null
23
22
  const storyData = api.getCurrentStoryData()
24
-
25
- useEffect(() => {
26
- const disposes = [
27
- api.on(NAME, (event: VisEvent) => {
23
+ if (!storyData) return null
24
+ api.emit(NAME, requestImageSnapshotResults(storyData))
25
+ async function getSnapshotResults() {
26
+ return new Promise<ImageSnapshotResults[]>((resolve) => {
27
+ api.once(NAME, (event: VisEvent) => {
28
28
  if (event.name !== storyData.name) return
29
29
  if (event.importPath !== storyData.importPath) return
30
-
31
30
  if (event.type === IMAGE_SNAPSHOT_RESULTS_RESPONSE) {
32
- setSnapshotResults(event.results)
31
+ resolve(event.results)
33
32
  }
34
- }),
35
- // api.on('UNIVERSAL_STORE:storybook/test', (event) => {
36
- // console.log('storybook/test', event)
37
- // }),
38
- ]
39
- if (storyData) api.emit(NAME, requestImageSnapshotResults(storyData))
40
- return () =>
41
- disposes.forEach((dispose) => {
42
- dispose()
43
33
  })
44
- }, [storyData])
45
-
46
- if (!active) return null
47
-
34
+ })
35
+ }
48
36
  return (
49
37
  <VisPanel
50
38
  active={active}
51
- snapshotResults={snapshotResults}
39
+ getSnapshotResults={getSnapshotResults}
52
40
  onRefresh={() => {
53
41
  if (storyData) api.emit(NAME, requestImageSnapshotResults(storyData))
54
42
  }}
package/src/overview.mdx CHANGED
@@ -190,6 +190,7 @@ Or use the triple-slash reference:
190
190
  "includes": [..., "types"]
191
191
  }
192
192
 
193
+ ```
193
194
 
194
195
  ```ts
195
196
  // types/storybook-addon-vis.d.ts