storybook-addon-datalayer-watcher
Version:
Get GTM's dataLayer events in Storybook
1 lines • 10.2 kB
Source Map (JSON)
{"version":3,"sources":["../src/manager.ts","../src/constants.ts","../src/components/Panel/Panel.tsx","../src/components/PanelContent/PanelContent.tsx","../src/utils/storybook.ts","../src/components/PanelContent/PanelContent.style.ts","../src/components/PanelActionsBar/PanelActionsBar.tsx","../src/components/PanelActionsBar/PanelActionsBar.style.ts"],"names":["addons","types","useAddonState","ADDON_ID","PANEL_ID","COUNT_SYMBOL","React","AddonPanel","useEffect","ObjectInspector","isEqual","withTheme","STORY_CHANGED","useChannel","getStorybookIframe","styled","Container","theme","EventItem","Counter","useCallback","Button","Bar","PanelActionsBar","handleClear","iFrame","PanelContent","scrollToRef","events","setEvents","ThemedInspector","key","props","eventsWithCount","acc","current","index","eventsOriginal","iframe","intervalId","newDataLayer","event","shouldDisplayCounter","Panel","Title","prefix","viewMode"],"mappings":"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","sourcesContent":["import { addons, types, useAddonState } from \"storybook/manager-api\";\n\nimport { ADDON_ID, PANEL_ID } from \"src/constants\";\nimport { Panel } from \"src/components\";\n\nfunction Title() {\n const [events] = useAddonState(ADDON_ID, []);\n\n let prefix = \"\";\n\n if (events.length > 0) {\n prefix = ` (${events.length})`;\n }\n\n return `👀 dataLayer watcher${prefix}`;\n}\n\naddons.register(ADDON_ID, () => {\n addons.add(PANEL_ID, {\n type: types.PANEL,\n title: Title,\n match: ({ viewMode }) => viewMode === \"story\",\n render: Panel,\n });\n});\n","export const ADDON_ID = \"storybook/storybook-addon-datalayer-watcher\";\nexport const PANEL_ID = `${ADDON_ID}/panel`;\n\nexport const COUNT_SYMBOL = Symbol(\"count\");\n","import React from \"react\";\nimport { AddonPanel } from \"storybook/internal/components\";\n\nimport { PanelContent } from \"../PanelContent/PanelContent\";\n\ninterface PanelProps {\n active: boolean;\n}\n\nexport const Panel: React.FC<PanelProps> = (props) => {\n return (\n <AddonPanel {...props}>\n <PanelContent />\n </AddonPanel>\n );\n};\n","import React, { useEffect } from \"react\";\n\nimport { ObjectInspector } from \"react-inspector\";\nimport { isEqual } from \"lodash\";\n\nimport { withTheme } from \"storybook/theming\";\nimport { STORY_CHANGED } from \"storybook/internal/core-events\";\nimport { useAddonState, useChannel } from \"storybook/manager-api\";\n\nimport { getStorybookIframe } from \"src/utils/storybook\";\nimport { ADDON_ID, COUNT_SYMBOL } from \"src/constants\";\n\nimport { Container, EventItem, Counter } from \"./PanelContent.style\";\nimport { InspectorProps } from \"./PanelConent.types\";\nimport { PanelActionsBar } from \"../PanelActionsBar/PanelActionsBar\";\n\nexport const PanelContent = () => {\n const scrollToRef = React.useRef<HTMLDivElement>(null);\n const [events, setEvents] = useAddonState(ADDON_ID, []);\n\n const ThemedInspector = withTheme(\n ({ key, theme, ...props }: InspectorProps) => (\n <ObjectInspector\n theme={theme.addonActionsTheme || \"chromeLight\"}\n table={false}\n expandLevel={0}\n sortObjectKeys\n showNonenumerable={false}\n key={key}\n {...props}\n />\n ),\n );\n\n const eventsWithCount: Record<string | symbol, unknown>[] = events.reduce(\n (acc, current, index, eventsOriginal) => {\n if (isEqual(eventsOriginal[index - 1], current)) {\n acc[acc.length - 1][COUNT_SYMBOL] += 1;\n } else {\n acc.push({ ...current, [COUNT_SYMBOL]: 1 });\n }\n return acc;\n },\n [],\n );\n\n useChannel({\n [STORY_CHANGED]: () => {\n const iframe = getStorybookIframe();\n\n if (iframe?.contentWindow) {\n iframe.contentWindow.dataLayer = [];\n }\n },\n });\n\n useEffect(() => {\n const intervalId = setInterval(() => {\n const newDataLayer = getStorybookIframe()?.contentWindow?.dataLayer ?? [];\n\n if (events.length === newDataLayer.length) {\n return;\n }\n\n if (isEqual(events, newDataLayer)) {\n return;\n }\n\n setEvents([...newDataLayer]);\n }, 100);\n\n return () => clearInterval(intervalId);\n }, [events, setEvents]);\n\n useEffect(() => {\n if (events.length) {\n scrollToRef.current?.scrollIntoView({ behavior: \"smooth\", block: \"end\" });\n }\n }, [events.length]);\n\n return (\n <>\n <PanelActionsBar />\n <Container>\n {eventsWithCount.map((event, index) => {\n const shouldDisplayCounter =\n typeof event[COUNT_SYMBOL] === \"number\" && event[COUNT_SYMBOL] > 1;\n\n return (\n <EventItem>\n {shouldDisplayCounter && <Counter>{event[COUNT_SYMBOL]}</Counter>}\n <ThemedInspector\n key={index}\n name={String(event.event)}\n data={event}\n />\n </EventItem>\n );\n })}\n <div ref={scrollToRef} />\n </Container>\n </>\n );\n};\n","type StorybookIframeWithDataLayer = HTMLIFrameElement & {\n contentWindow: { dataLayer?: Record<string, unknown>[] };\n};\n\ntype StorybookIframe = StorybookIframeWithDataLayer | undefined;\n\nexport const getStorybookIframe = () => {\n return document.getElementById(\"storybook-preview-iframe\") as StorybookIframe;\n};\n","import { styled } from \"storybook/theming\";\n\nexport const Container = styled.div`\n display: flex;\n flex-direction: column;\n justify-content: flex-start;\n align-items: flex-start;\n gap: 24px;\n margin: 12px;\n width: 100%;\n color: ${({ theme }) => theme.color.positive};\n`;\n\nexport const EventItem = styled.div`\n display: flex;\n flex-direction: row;\n justify-content: flex-start;\n align-items: flex-start;\n gap: 12px;\n width: 100%;\n`;\n\nexport const Counter = styled.span`\n padding: 0 8px;\n border-radius: 4px;\n font-size: ${({ theme }) => theme.typography.size.code};\n background-color: ${({ theme }) => theme.color.dark};\n color: ${({ theme }) => theme.color.lightest};\n`;\n","import React, { useCallback } from \"react\";\nimport { Button } from \"storybook/internal/components\";\n\nimport { getStorybookIframe } from \"src/utils/storybook\";\n\nimport { Bar, Container } from \"./PanelActionsBar.style\";\n\nexport const PanelActionsBar = () => {\n const handleClear = useCallback(() => {\n const iFrame = getStorybookIframe();\n\n if (iFrame?.contentWindow) {\n iFrame.contentWindow.dataLayer = [];\n }\n }, []);\n\n return (\n <Container>\n <Bar>\n <Button onClick={handleClear}>🗑️ Clear dataLayer</Button>\n </Bar>\n </Container>\n );\n};\n","import { styled } from \"storybook/theming\";\n\nexport const Container = styled.div`\n position: sticky;\n top: 0;\n height: 40px;\n width: 100%;\n background-color: ${({ theme }) => theme.background.content};\n z-index: 9999999;\n`;\n\nexport const Bar = styled.div`\n display: flex;\n flex-direction: row;\n justify-content: flex-end;\n align-items: center;\n gap: 12px;\n padding: 0 12px;\n\n height: 40px;\n width: 100%;\n`;\n"]}