@qld-gov-au/qgds-bootstrap5 2.1.10 → 2.1.12

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.
Files changed (185) hide show
  1. package/.esbuild/plugins/qgds-plugin-clean-output-folders.js +2 -2
  2. package/.esbuild/plugins/qgds-plugin-story-list-builder.js +112 -0
  3. package/.storybook/DocumentationTemplate.mdx +47 -0
  4. package/.storybook/addons/qgds-multi-code-panels/components/Panel.tsx +231 -0
  5. package/.storybook/addons/qgds-multi-code-panels/constants.js +8 -0
  6. package/.storybook/addons/qgds-multi-code-panels/manager.tsx +15 -0
  7. package/.storybook/addons/qgds-multi-code-panels/preset.js +12 -0
  8. package/.storybook/codeRefsDecorator.js +87 -0
  9. package/.storybook/customMDXComponents.jsx +284 -0
  10. package/.storybook/main.mjs +16 -11
  11. package/.storybook/manager.js +2 -2
  12. package/.storybook/preview.js +39 -1
  13. package/dist/assets/components/bs5/directionLinks/directionLinks.hbs +0 -1
  14. package/dist/assets/components/bs5/head/head.hbs +1 -1
  15. package/dist/assets/components/bs5/pagination/pagination.hbs +0 -7
  16. package/dist/assets/components/bs5/searchInput/searchInput.hbs +1 -1
  17. package/dist/assets/css/qld.bootstrap.css +2 -2
  18. package/dist/assets/css/qld.bootstrap.css.map +3 -3
  19. package/dist/assets/css/qld.bootstrap.legacy.css +2 -2
  20. package/dist/assets/css/qld.bootstrap.legacy.css.map +3 -3
  21. package/dist/assets/js/handlebars.helpers.bundle.js +1 -1
  22. package/dist/assets/js/handlebars.helpers.bundle.js.map +2 -2
  23. package/dist/assets/js/handlebars.init.min.js +4 -12
  24. package/dist/assets/js/handlebars.init.min.js.map +2 -2
  25. package/dist/assets/js/handlebars.partials.js +4 -12
  26. package/dist/assets/js/handlebars.partials.js.map +2 -2
  27. package/dist/assets/js/qld.bootstrap.min.js +1 -1
  28. package/dist/assets/node/handlebars.init.min.js +3 -11
  29. package/dist/assets/node/handlebars.init.min.js.map +2 -2
  30. package/dist/components/bs5/directionLinks/directionLinks.hbs +0 -1
  31. package/dist/components/bs5/head/head.hbs +1 -1
  32. package/dist/components/bs5/pagination/pagination.hbs +0 -7
  33. package/dist/components/bs5/searchInput/searchInput.hbs +1 -1
  34. package/dist/package.json +6 -6
  35. package/esbuild.js +2 -0
  36. package/package.json +6 -6
  37. package/src/components/bs5/accordion/accordion.stories.js +6 -13
  38. package/src/components/bs5/accordion/metadata.json +15 -0
  39. package/src/components/bs5/backToTop/backToTop.stories.js +10 -14
  40. package/src/components/bs5/backToTop/manifest.json +15 -0
  41. package/src/components/bs5/backToTop/metadata.json +15 -0
  42. package/src/components/bs5/banner/banner.stories.js +16 -1
  43. package/src/components/bs5/banner/metadata.json +16 -0
  44. package/src/components/bs5/blockquote/blockquote.stories.js +20 -11
  45. package/src/components/bs5/blockquote/metadata.json +15 -0
  46. package/src/components/bs5/breadcrumbs/breadcrumbs.stories.js +6 -4
  47. package/src/components/bs5/breadcrumbs/metadata.json +16 -0
  48. package/src/components/bs5/button/button.stories.js +13 -5
  49. package/src/components/bs5/button/metadata.json +15 -0
  50. package/src/components/bs5/callToAction/callToAction.stories.js +6 -5
  51. package/src/components/bs5/callToAction/metadata.json +15 -0
  52. package/src/components/bs5/callout/callout.stories.js +6 -5
  53. package/src/components/bs5/callout/metadata.json +16 -0
  54. package/src/components/bs5/card/card--icon-list-footer.stories.js +4 -4
  55. package/src/components/bs5/card/card--multi-action.stories.js +6 -0
  56. package/src/components/bs5/card/card--no-action.stories.js +7 -0
  57. package/src/components/bs5/card/card--single-action.stories.js +7 -0
  58. package/src/components/bs5/card/metadata.json +15 -0
  59. package/src/components/bs5/containerLayout/containerLayout.stories.js +6 -0
  60. package/src/components/bs5/containerLayout/metadata.json +1 -0
  61. package/src/components/bs5/contentFooter/contentFooter.stories.js +26 -20
  62. package/src/components/bs5/contentFooter/metadata.json +15 -0
  63. package/src/components/bs5/correctincorrect/correctincorrect.stories.js +53 -38
  64. package/src/components/bs5/correctincorrect/metadata.json +14 -0
  65. package/src/components/bs5/dateinput/Dateinput.stories.js +6 -0
  66. package/src/components/bs5/dateinput/metadata.json +14 -0
  67. package/src/components/bs5/directionLinks/directionLinks.hbs +0 -1
  68. package/src/components/bs5/directionLinks/directionLinks.stories.js +4 -4
  69. package/src/components/bs5/directionLinks/metadata.json +16 -0
  70. package/src/components/bs5/footer/footer.stories.js +7 -1
  71. package/src/components/bs5/footer/metadata.json +15 -0
  72. package/src/components/bs5/formcheck/metadata.json +15 -0
  73. package/src/components/bs5/formcheck/stories/checkbox/checkbox.stories.js +5 -1
  74. package/src/components/bs5/formcheck/stories/radio/radio.stories.js +12 -1
  75. package/src/components/bs5/globalAlert/globalAlert.stories.js +9 -0
  76. package/src/components/bs5/globalAlert/metadata.json +15 -0
  77. package/src/components/bs5/head/head.stories.js +33 -14
  78. package/src/components/bs5/header/header.stories.js +10 -0
  79. package/src/components/bs5/header/metadata.json +15 -0
  80. package/src/components/bs5/icons/icons.stories.js +5 -0
  81. package/src/components/bs5/icons/metadata.json +15 -0
  82. package/src/components/bs5/image/image.stories.js +9 -0
  83. package/src/components/bs5/image/metadata.json +15 -0
  84. package/src/components/bs5/inpageAlert/inpageAlert.stories.js +9 -2
  85. package/src/components/bs5/inpageAlert/metadata.json +15 -0
  86. package/src/components/bs5/inpagenav/inpagenav.stories.js +8 -1
  87. package/src/components/bs5/inpagenav/metadata.json +15 -0
  88. package/src/components/bs5/link/link.stories.js +5 -5
  89. package/src/components/bs5/link/metadata.json +15 -0
  90. package/src/components/bs5/linkColumns/linkColumns.stories.js +4 -4
  91. package/src/components/bs5/linkColumns/metadata.json +15 -0
  92. package/src/components/bs5/logo/logo.stories.js +40 -5
  93. package/src/components/bs5/logo/metadata.json +16 -0
  94. package/src/components/bs5/metaDcTerms/metaDcTerms.stories.js +14 -9
  95. package/src/components/bs5/metaOpenGraph/metaOpenGraph.stories.js +14 -9
  96. package/src/components/bs5/modal/metadata.json +15 -0
  97. package/src/components/bs5/modal/modal.stories.js +64 -60
  98. package/src/components/bs5/navbar/metadata.json +15 -0
  99. package/src/components/bs5/navbar/navbar.stories.js +9 -4
  100. package/src/components/bs5/pageLayout/metadata.json +1 -0
  101. package/src/components/bs5/pageLayout/pageLayout.stories.js +1 -0
  102. package/src/components/bs5/pagination/metadata.json +15 -0
  103. package/src/components/bs5/pagination/pagination.hbs +0 -7
  104. package/src/components/bs5/pagination/pagination.stories.js +5 -0
  105. package/src/components/bs5/promotionalPanel/metadata.json +15 -0
  106. package/src/components/bs5/promotionalPanel/promotionalPanel.stories.js +4 -4
  107. package/src/components/bs5/quickexit/metadata.json +15 -0
  108. package/src/components/bs5/quickexit/quickexit.stories.js +7 -0
  109. package/src/components/bs5/searchInput/__snapshots__/searchInput.test.js.snap +4 -4
  110. package/src/components/bs5/searchInput/metadata.json +15 -0
  111. package/src/components/bs5/searchInput/searchInput.hbs +1 -1
  112. package/src/components/bs5/searchInput/searchInput.scss +88 -5
  113. package/src/components/bs5/searchInput/searchInput.stories.js +5 -0
  114. package/src/components/bs5/select/Select.stories.js +8 -1
  115. package/src/components/bs5/select/metadata.json +15 -0
  116. package/src/components/bs5/sidenav/metadata.json +15 -0
  117. package/src/components/bs5/sidenav/sidenav.stories.js +5 -0
  118. package/src/components/bs5/skiplinks/skipLinks.stories.js +5 -0
  119. package/src/components/bs5/spinner/Spinner.stories.js +8 -0
  120. package/src/components/bs5/spinner/metadata.json +15 -0
  121. package/src/components/bs5/table/metadata.json +15 -0
  122. package/src/components/bs5/table/table.stories.js +139 -61
  123. package/src/components/bs5/tabs/metadata.json +15 -0
  124. package/src/components/bs5/tabs/tabs.stories.js +8 -0
  125. package/src/components/bs5/tag/metadata.json +15 -0
  126. package/src/components/bs5/tag/tag--large.stories.js +7 -0
  127. package/src/components/bs5/tag/tag--standard.stories.js +12 -5
  128. package/src/components/bs5/tag/tag--status.stories.js +4 -0
  129. package/src/components/bs5/tag/tag.stories.js +10 -0
  130. package/src/components/bs5/textarea/Textarea.stories.js +8 -1
  131. package/src/components/bs5/textarea/metadata.json +15 -0
  132. package/src/components/bs5/textbox/Textbox.stories.js +11 -1
  133. package/src/components/bs5/textbox/metadata.json +15 -0
  134. package/src/components/bs5/typography/metadata.json +1 -0
  135. package/src/components/bs5/typography/typography.stories.js +4 -0
  136. package/src/components/bs5/video/metadata.json +15 -0
  137. package/src/components/bs5/video/video.stories.js +5 -5
  138. package/src/components/common/focus-styles/focusStyles.stories.js +13 -9
  139. package/src/js/QGDSComponent.js +0 -1
  140. package/src/js/handlebars.helpers.js +1 -0
  141. package/src/stories/Introduction/development.mdx +136 -0
  142. package/src/stories/Introduction/how-to-use.mdx +272 -0
  143. package/src/stories/Introduction.mdx +95 -13
  144. package/src/{components/bs5/accordion/Accordion.mdx → stories/archive/__Accordion.mdx} +3 -3
  145. package/src/stories/archive/__Blockquote.mdx +13 -0
  146. package/src/stories/archive/__BlockquoteCanvas.mdx +47 -0
  147. package/src/stories/archive/__backToTop.orig.mdx +13 -0
  148. package/src/stories/archive/__blockquote.stories.bak.js +132 -0
  149. package/src/stories/component-list.json +627 -0
  150. package/src/templates/data/component.metadata.template.json +15 -0
  151. package/src/templates/index.html +40 -37
  152. /package/src/{components/bs5/banner/Banner.mdx → stories/archive/__Banner.mdx} +0 -0
  153. /package/src/{components/bs5/blockquote/Blockquote.mdx → stories/archive/__Blockquote.bak.mdx} +0 -0
  154. /package/src/{components/bs5/breadcrumbs/Breadcrumbs.mdx → stories/archive/__Breadcrumbs.mdx} +0 -0
  155. /package/src/{components/bs5/button/Button.mdx → stories/archive/__Button.mdx} +0 -0
  156. /package/src/{components/bs5/callout/Callout.mdx → stories/archive/__Callout.mdx} +0 -0
  157. /package/src/{components/bs5/card/Card.mdx → stories/archive/__Card.mdx} +0 -0
  158. /package/src/{components/bs5/formcheck/stories/checkbox/Checkbox.mdx → stories/archive/__Checkbox.mdx} +0 -0
  159. /package/src/{components/bs5/dateinput/Dateinput.mdx → stories/archive/__Dateinput.mdx} +0 -0
  160. /package/src/{components/bs5/directionLinks/DirectionLinks.mdx → stories/archive/__DirectionLinks.mdx} +0 -0
  161. /package/src/{components/bs5/footer/Footer.mdx → stories/archive/__Footer.mdx} +0 -0
  162. /package/src/{components/bs5/globalAlert/GlobalAlert.mdx → stories/archive/__GlobalAlert.mdx} +0 -0
  163. /package/src/{components/bs5/header/Header.mdx → stories/archive/__Header.mdx} +0 -0
  164. /package/src/{components/bs5/image/Image.mdx → stories/archive/__Image.mdx} +0 -0
  165. /package/src/{components/bs5/inpageAlert/InpageAlert.mdx → stories/archive/__InpageAlert.mdx} +0 -0
  166. /package/src/{components/bs5/inpagenav/Inpagenav.mdx → stories/archive/__Inpagenav.mdx} +0 -0
  167. /package/src/{components/bs5/logo/Logo.mdx → stories/archive/__Logo.mdx} +0 -0
  168. /package/src/{components/bs5/modal/Modal.mdx → stories/archive/__Modal.mdx} +0 -0
  169. /package/src/{components/bs5/pagination/Pagination.mdx → stories/archive/__Pagination.mdx} +0 -0
  170. /package/src/{components/bs5/promotionalPanel/PromotionalPanel.mdx → stories/archive/__PromotionalPanel.mdx} +0 -0
  171. /package/src/{components/bs5/formcheck/stories/radio/Radio.mdx → stories/archive/__Radio.mdx} +0 -0
  172. /package/src/{components/bs5/searchInput/SearchInput.mdx → stories/archive/__SearchInput.mdx} +0 -0
  173. /package/src/{components/bs5/sidenav/Sidenav.mdx → stories/archive/__Sidenav.mdx} +0 -0
  174. /package/src/{components/bs5/skiplinks/SkipLinks.mdx → stories/archive/__SkipLinks.mdx} +0 -0
  175. /package/src/{components/bs5/table/Table.mdx → stories/archive/__Table.mdx} +0 -0
  176. /package/src/{components/bs5/tabs/Tabs.mdx → stories/archive/__Tabs.mdx} +0 -0
  177. /package/src/{components/bs5/tag/Tag.mdx → stories/archive/__Tag.mdx} +0 -0
  178. /package/src/{components/bs5/typography/Typography.mdx → stories/archive/__Typography.mdx} +0 -0
  179. /package/src/{components/bs5/video/Video.mdx → stories/archive/__Video.mdx} +0 -0
  180. /package/src/{components/bs5/backToTop/backToTop.mdx → stories/archive/__backToTop.mdx} +0 -0
  181. /package/src/{components/bs5/callToAction/callToAction.mdx → stories/archive/__callToAction.mdx} +0 -0
  182. /package/src/{components/bs5/accordion/mdx/_designResources.mdx → stories/archive/__designResources.mdx} +0 -0
  183. /package/src/{components/bs5/link/link.mdx → stories/archive/__link.mdx} +0 -0
  184. /package/src/{components/bs5/linkColumns/linkColumns.mdx → stories/archive/__linkColumns.mdx} +0 -0
  185. /package/src/{components/bs5/accordion/mdx/_usageInstructions.mdx → stories/archive/__usageInstructions.mdx} +0 -0
@@ -13,11 +13,11 @@ export default function cleanFoldersPlugin() {
13
13
  const { outdir, outfile } = build.initialOptions;
14
14
 
15
15
  if (outdir && fs.existsSync(outdir)) {
16
- fs.rmSync(outdir, { recursive: true });
16
+ fs.rmSync(outdir, { recursive: true, force: true, maxRetries: 3 });
17
17
  }
18
18
 
19
19
  if (outfile && fs.existsSync(outfile)) {
20
- fs.rmSync(outfile);
20
+ fs.rmSync(outfile, { force: true });
21
21
  }
22
22
 
23
23
  // // Clean the docs folder
@@ -0,0 +1,112 @@
1
+ // qgds-plugin-story-list-builder.js
2
+ import fs from "fs";
3
+ import path from "path";
4
+ import { fileURLToPath } from "url";
5
+
6
+ const __filename = fileURLToPath(import.meta.url);
7
+ const __dirname = path.dirname(__filename);
8
+
9
+ const COMPONENTS_DIR = path.resolve(__dirname, "./../../src/components/bs5");
10
+ const STORY_LIST_FILE = path.resolve(
11
+ __dirname,
12
+ "../../src/stories/component-list.json",
13
+ );
14
+
15
+ function getAllMetadataFiles(dirPath, arrayOfFiles) {
16
+ const files = fs.readdirSync(dirPath, { withFileTypes: true });
17
+
18
+ arrayOfFiles = arrayOfFiles || [];
19
+
20
+ files.forEach((file) => {
21
+ if (file.isDirectory()) {
22
+ arrayOfFiles = getAllMetadataFiles(
23
+ path.join(dirPath, file.name),
24
+ arrayOfFiles,
25
+ );
26
+ } else if (file.isFile() && file.name === "metadata.json") {
27
+ arrayOfFiles.push(path.join(dirPath, file.name));
28
+ }
29
+ });
30
+
31
+ return arrayOfFiles;
32
+ }
33
+
34
+ function readMetadata(filePath) {
35
+ try {
36
+ const content = fs.readFileSync(filePath, "utf8");
37
+ const metadata = JSON.parse(content);
38
+
39
+ // Try to find corresponding .stories.js file
40
+ const componentDir = path.dirname(filePath);
41
+ const componentName = path.basename(componentDir);
42
+ const storyFiles = fs
43
+ .readdirSync(componentDir)
44
+ .filter((f) => f.endsWith(".stories.js"));
45
+
46
+ let storyTitle = null;
47
+ if (storyFiles.length > 0) {
48
+ const storyPath = path.join(componentDir, storyFiles[0]);
49
+ const storyContent = fs.readFileSync(storyPath, "utf8");
50
+ const titleMatch = storyContent.match(/title:\s*["']([^"']+)["']/);
51
+ storyTitle = titleMatch ? titleMatch[1] : null;
52
+ }
53
+
54
+ return {
55
+ id: metadata.id,
56
+ title: metadata.title,
57
+ title_uikit: metadata.title_uikit,
58
+ type: metadata.type,
59
+ scope: metadata.scope,
60
+ group: metadata.group || "Components",
61
+ status: metadata.status,
62
+ description: metadata.description,
63
+ refs: metadata.refs,
64
+ storyTitle: storyTitle,
65
+ storyPath: storyTitle
66
+ ? `/docs/${storyTitle.toLowerCase().replace(/\s+/g, "-").replace(/\//g, "-").replace(/\./g, "")}`
67
+ : null,
68
+ };
69
+ } catch (error) {
70
+ console.error(`Error reading ${filePath}:`, error);
71
+ return null;
72
+ }
73
+ }
74
+
75
+ export default function QGDSStoryListBuilderPlugin() {
76
+ return {
77
+ name: "story-list-builder",
78
+ setup(build) {
79
+ build.onStart(async () => {
80
+ const files = getAllMetadataFiles(COMPONENTS_DIR);
81
+ // Sort files alphabetically to ensure deterministic ordering
82
+ files.sort((a, b) =>
83
+ path
84
+ .basename(path.dirname(a))
85
+ .localeCompare(path.basename(path.dirname(b))),
86
+ );
87
+
88
+ const componentList = [];
89
+
90
+ for (const file of files) {
91
+ const metadata = readMetadata(file);
92
+ if (metadata && metadata.status === "Published") {
93
+ componentList.push(metadata);
94
+ }
95
+ }
96
+
97
+ //Sort component list by title
98
+ componentList.sort((a, b) => a.title.localeCompare(b.title));
99
+
100
+ // Write as JSON for easy import
101
+ fs.writeFileSync(
102
+ STORY_LIST_FILE,
103
+ JSON.stringify(componentList, null, 2),
104
+ );
105
+
106
+ console.log(
107
+ `${componentList.length} components found and written to ${STORY_LIST_FILE}`,
108
+ );
109
+ });
110
+ },
111
+ };
112
+ }
@@ -0,0 +1,47 @@
1
+ import {
2
+ Meta,
3
+ Title,
4
+ Primary,
5
+ Canvas,
6
+ Controls,
7
+ Stories,
8
+ Source,
9
+ useOf,
10
+ } from "@storybook/addon-docs/blocks";
11
+
12
+ import {
13
+ ComponentMeta,
14
+ CodeReferencesInstructions,
15
+ DesignResources,
16
+ Notes,
17
+ } from "./customMDXComponents.jsx";
18
+
19
+ <Meta isTemplate />
20
+
21
+ <div
22
+ style={{
23
+ display: "flex",
24
+ flexDirection: "row",
25
+ justifyContent: "space-between",
26
+ gap: "1rem",
27
+ marginBottom: "2rem",
28
+ }}
29
+ >
30
+
31
+ <Title />
32
+ </div>
33
+
34
+ <div>
35
+ <ComponentMeta />
36
+ </div>
37
+
38
+ ## Example
39
+
40
+ <Primary />
41
+
42
+ <Notes title="Notes" />
43
+
44
+ <Stories title="Variants" includePrimary={false} />
45
+
46
+ <DesignResources title="Design resources" />
47
+ <CodeReferencesInstructions title="How to use" />
@@ -0,0 +1,231 @@
1
+ import React, { memo, useState } from "react";
2
+
3
+ import prettier from "prettier/standalone";
4
+ import parserHtml from "prettier/parser-html";
5
+ import {
6
+ AddonPanel,
7
+ TabsState,
8
+ P,
9
+ SyntaxHighlighter,
10
+ } from "storybook/internal/components";
11
+ import { useChannel, useStorybookApi } from "storybook/manager-api";
12
+ import { useTheme } from "storybook/theming";
13
+
14
+ interface PanelProps {
15
+ active?: boolean;
16
+ }
17
+
18
+ interface ShowTabs {
19
+ template?: boolean;
20
+ json?: boolean;
21
+ htmlstory?: boolean;
22
+ htmlcomponent?: boolean;
23
+ howtouse?: boolean;
24
+ notes?: boolean;
25
+ }
26
+
27
+ // Single interface for both incoming data and state
28
+ interface PanelContent {
29
+ template: string;
30
+ htmlstory: string;
31
+ htmlcomponent: string;
32
+ json: any;
33
+ notes: string;
34
+ name: string;
35
+ partialname: string;
36
+ showPanel: boolean;
37
+ showTabs: ShowTabs;
38
+ metadata: any;
39
+ }
40
+
41
+ let lastCodeRefs: PanelContent | null = null;
42
+
43
+ // Format code with Prettier
44
+ const formatCode = async (code: string): Promise<string> => {
45
+ if (!code) return "";
46
+
47
+ try {
48
+ return await prettier.format(code, {
49
+ parser: "html",
50
+ plugins: [parserHtml],
51
+ printWidth: 120,
52
+ tabWidth: 2,
53
+ useTabs: false,
54
+ htmlWhitespaceSensitivity: "ignore",
55
+ });
56
+ } catch (error) {
57
+ console.warn("Failed to format:", error);
58
+ return code;
59
+ }
60
+ };
61
+
62
+ export const Panel: React.FC<PanelProps> = memo(function MyPanel({ active }) {
63
+ const api = useStorybookApi();
64
+ const storyId = api.getCurrentStoryData()?.id || "N/A";
65
+
66
+ const theme = useTheme();
67
+ const [content, setContent] = useState<PanelContent | null>(lastCodeRefs);
68
+
69
+ useChannel({
70
+ CODEREFS_UPDATE: async (data: PanelContent) => {
71
+ // If showPanel is false or no showTabs defined, clear the content
72
+ if (!data.showPanel || !data.showTabs) {
73
+ setContent(null);
74
+ lastCodeRefs = null;
75
+ return;
76
+ }
77
+
78
+ const [formattedTemplate, formattedHtmlStory, formattedHtmlComponent] =
79
+ await Promise.all([
80
+ formatCode(data.template),
81
+ formatCode(data.htmlstory),
82
+ formatCode(data.htmlcomponent),
83
+ ]);
84
+
85
+ const newContent = {
86
+ template: formattedTemplate,
87
+ htmlstory: formattedHtmlStory,
88
+ htmlcomponent: formattedHtmlComponent,
89
+ json: data.json,
90
+ notes: data.notes,
91
+ name: data.name,
92
+ partialname: data.partialname,
93
+ metadata: data.metadata,
94
+ showPanel: data.showPanel,
95
+ showTabs: data.showTabs,
96
+ };
97
+ setContent(newContent);
98
+ lastCodeRefs = newContent;
99
+ },
100
+ });
101
+
102
+ // Build visible tabs array
103
+ const buildTabs = () => {
104
+ if (!content) return [];
105
+
106
+ const tabs: React.ReactElement[] = [];
107
+
108
+ if (content.showTabs.htmlstory !== false) {
109
+ tabs.push(
110
+ <div
111
+ key="htmlstory"
112
+ id="htmlstory"
113
+ title="HTML (Story)"
114
+ color={theme.color.defaultText}
115
+ >
116
+ <div style={{ padding: "20px" }}>
117
+ <SyntaxHighlighter language="html" copyable={true}>
118
+ {content.htmlstory}
119
+ </SyntaxHighlighter>
120
+ </div>
121
+ </div>,
122
+ );
123
+ }
124
+
125
+ if (content.showTabs.htmlcomponent !== false) {
126
+ tabs.push(
127
+ <div
128
+ key="htmlcomponent"
129
+ id="htmlcomponent"
130
+ title="HTML (Component)"
131
+ color={theme.color.defaultText}
132
+ >
133
+ <div style={{ padding: "20px" }}>
134
+ <SyntaxHighlighter language="html" copyable={true}>
135
+ {content.htmlcomponent}
136
+ </SyntaxHighlighter>
137
+ </div>
138
+ </div>,
139
+ );
140
+ }
141
+
142
+ if (content.showTabs.template !== false) {
143
+ tabs.push(
144
+ <div
145
+ key="template"
146
+ id="template"
147
+ title="Handlebars"
148
+ color={theme.color.defaultText}
149
+ >
150
+ <div style={{ padding: "20px" }}>
151
+ <SyntaxHighlighter language="html" copyable={true}>
152
+ {content.template}
153
+ </SyntaxHighlighter>
154
+ </div>
155
+ </div>,
156
+ );
157
+ }
158
+
159
+ if (content.showTabs.json !== false) {
160
+ tabs.push(
161
+ <div
162
+ key="params"
163
+ id="params"
164
+ title="Data"
165
+ color={theme.color.defaultText}
166
+ >
167
+ <div style={{ padding: "20px" }}>
168
+ <SyntaxHighlighter language="json" copyable={true}>
169
+ {JSON.stringify(content.json, null, 2)}
170
+ </SyntaxHighlighter>
171
+ </div>
172
+ </div>,
173
+ );
174
+ }
175
+
176
+ if (content.showTabs.notes) {
177
+ tabs.push(
178
+ <div
179
+ key="notes"
180
+ id="notes"
181
+ title="Notes"
182
+ color={theme.color.defaultText}
183
+ >
184
+ <div style={{ padding: "20px" }}>
185
+ <P>{content.notes}</P>
186
+ </div>
187
+ </div>,
188
+ );
189
+ }
190
+
191
+ if (content.showTabs.howtouse) {
192
+ tabs.push(
193
+ <div
194
+ key="howtouse"
195
+ id="howtouse"
196
+ title="Use"
197
+ color={theme.color.defaultText}
198
+ >
199
+ <div style={{ padding: "20px" }}>
200
+ <SyntaxHighlighter language="typescript" copyable={true}>
201
+ {`const data = ${JSON.stringify(content.json, undefined, 2)}; \n\nconst html = Handlebars.compile(\`{{> ${content.partialname} }}\`)(data);`}
202
+ </SyntaxHighlighter>
203
+ </div>
204
+ </div>,
205
+ );
206
+ }
207
+
208
+ return tabs;
209
+ };
210
+
211
+ const tabs = buildTabs();
212
+ const initialTab = tabs.length > 0 ? tabs[0].props.id : "template";
213
+
214
+ return (
215
+ <AddonPanel active={active ?? false}>
216
+ {content === null || tabs.length === 0 ? (
217
+ <div style={{ padding: "20px", color: "#999" }}>
218
+ Code references are disabled for this story.
219
+ </div>
220
+ ) : (
221
+ <TabsState
222
+ initial={initialTab}
223
+ backgroundColor={theme.background.hoverable}
224
+ key={`story-codetabs-${storyId}`}
225
+ >
226
+ {tabs}
227
+ </TabsState>
228
+ )}
229
+ </AddonPanel>
230
+ );
231
+ });
@@ -0,0 +1,8 @@
1
+ export const ADDON_ID = "qgds-code-tabs";
2
+ export const PANEL_ID = `${ADDON_ID}/panel`;
3
+ export const KEY = `my-addon`;
4
+
5
+ export const EVENTS = {
6
+ RESULT: `${ADDON_ID}/result`,
7
+ REQUEST: `${ADDON_ID}/request`,
8
+ };
@@ -0,0 +1,15 @@
1
+ import React from "react";
2
+ import { addons, types } from "storybook/manager-api";
3
+ import { ADDON_ID, PANEL_ID } from "./constants";
4
+ import { Panel } from "./components/Panel";
5
+
6
+ // Register the addon
7
+ addons.register(ADDON_ID, (api) => {
8
+ // Register a panel on each story view
9
+ addons.add(PANEL_ID, {
10
+ type: types.PANEL,
11
+ title: "Code References",
12
+ match: ({ viewMode }) => viewMode === "story",
13
+ render: ({ active }) => active && <Panel active={active} />,
14
+ });
15
+ });
@@ -0,0 +1,12 @@
1
+ // This file is loaded during Storybook's Node.js configuration phase
2
+ // It must not contain JSX or browser-only code
3
+
4
+ import { join, dirname } from "path";
5
+ import { fileURLToPath } from "url";
6
+
7
+ const __filename = fileURLToPath(import.meta.url);
8
+ const __dirname = dirname(__filename);
9
+
10
+ export function managerEntries(entry = []) {
11
+ return [...entry, join(__dirname, "./manager.tsx")];
12
+ }
@@ -0,0 +1,87 @@
1
+ import { useEffect } from "storybook/preview-api";
2
+ import { addons } from "storybook/preview-api";
3
+ import Handlebars from "handlebars";
4
+
5
+ /**
6
+ * Channel Communication Decorator
7
+ *
8
+ * Establishes a channel connection between the Storybook preview (inner iframe)
9
+ * and Storybook manager (outer frame) to send coderefs data to the
10
+ * "Code References" panel whenever a story is rendered or updated.
11
+ *
12
+ * @fires CODEREFS_UPDATE - Emits payload to manager with template, json, html, name, notes, and metadata
13
+ */
14
+ export const withCodeRefs = (Story, context) => {
15
+ const { args, parameters } = context;
16
+
17
+ const coderefs = parameters.coderefs || {};
18
+ const metadata = coderefs?.metadata || {};
19
+ const storyArgs = coderefs?.args || args;
20
+
21
+ useEffect(() => {
22
+ const channel = addons.getChannel();
23
+
24
+ // If coderefs.show is explicitly false, send "hide" signal and exit
25
+ if (coderefs.show === false) {
26
+ channel.emit("CODEREFS_UPDATE", { showPanel: false });
27
+ return;
28
+ }
29
+
30
+ // Default tabs to show
31
+ const showtabs = {
32
+ htmlstory: coderefs.tabs?.htmlstory !== false,
33
+ htmlcomponent: coderefs.tabs?.htmlcomponent !== false,
34
+ json: coderefs.tabs?.json !== false,
35
+ template: coderefs.tabs?.template !== false,
36
+ howtouse: coderefs.tabs?.howtouse !== false,
37
+ notes: coderefs.tabs?.notes !== undefined,
38
+ };
39
+
40
+ // Check if any tabs are visible
41
+ const hasVisibleTabs = Object.values(showtabs).some(Boolean);
42
+
43
+ // If no tabs to show, send "hide" signal and exit
44
+ if (!hasVisibleTabs) {
45
+ channel.emit("CODEREFS_UPDATE", { showPanel: false });
46
+ return;
47
+ }
48
+
49
+ // Get the story's rendered HTML
50
+
51
+ // Full Story HTML
52
+ const htmlstoryContent = Story(args, context);
53
+
54
+ // Component only HTML
55
+ const hbspartial = Handlebars.partials[coderefs.partialname] || false;
56
+
57
+ if (!hbspartial) {
58
+ console.warn(
59
+ `[CODEREFS] Partial "${coderefs.partialname}" not found in Handlebars partials.`,
60
+ );
61
+ }
62
+
63
+ const htmlcomponentContent =
64
+ typeof hbspartial === "string"
65
+ ? Handlebars.compile(hbspartial)(args)
66
+ : "Missing partialname in story config, or component does use a handlebars template.";
67
+
68
+ // Data we're sending from preview frame to manager frame
69
+ const payload = {
70
+ showPanel: true,
71
+ showTabs: showtabs,
72
+ template: hbspartial || "No template available",
73
+ json: storyArgs,
74
+ htmlstory: htmlstoryContent,
75
+ htmlcomponent: htmlcomponentContent,
76
+ notes: coderefs.tabs?.notes || "Nil",
77
+ name: context.name || "Unknown",
78
+ partialname: coderefs.partialname || "Unknown",
79
+ metadata: metadata,
80
+ };
81
+
82
+ console.log("[PREVIEW] Sending CODEREFS_UPDATE:", payload);
83
+ channel.emit("CODEREFS_UPDATE", payload);
84
+ }, [args, parameters.coderefs]);
85
+
86
+ return Story();
87
+ };