@qld-gov-au/qgds-bootstrap5 2.0.4 → 2.0.5
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/.esbuild/helpers/scssOverrideTheme.js +40 -0
- package/.esbuild/plugins/qgds-plugin-handlebar-partial-builder.js +4 -0
- package/.storybook/dynamicThemeDecorator.js +155 -0
- package/.storybook/main.js +6 -0
- package/.storybook/preview.js +20 -1
- package/README.md +26 -18
- package/dist/README.md +26 -18
- package/dist/assets/components/bs5/head/head.hbs +1 -1
- package/dist/assets/css/qld.bootstrap.css +1 -1
- package/dist/assets/css/qld.bootstrap.css.map +3 -3
- package/dist/assets/js/handlebars.init.min.js +1 -1
- package/dist/assets/node/handlebars.init.min.js +1 -1
- package/dist/components/bs5/head/head.hbs +1 -1
- package/dist/package.json +6 -2
- package/esbuild.js +34 -9
- package/package.json +6 -2
- package/src/components/bs5/card/card--icon-list-footer.stories.js +171 -0
- package/src/components/bs5/card/card.scss +15 -0
- package/src/components/bs5/header/header.scss +8 -0
- package/src/components/bs5/pageLayout/pageLayout.stories.js +2 -2
- package/src/components/bs5/searchInput/searchInput.test.js +21 -5
- package/src/css/main.masterbrand.scss +137 -0
- package/src/css/qld-tokens.scss +95 -0
- package/src/css/qld-variables.scss +10 -3
- package/src/css/readme.md +6 -1
- package/src/css/themes/masterbrand.scss +109 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Creates a temporary main.<themeVar>.scss file with the theme scss import injected after qld-variables import.
|
|
6
|
+
* Only creates/updates the file if content has changed or file doesn't exist.
|
|
7
|
+
* Returns the path to the temp file, or null if not created.
|
|
8
|
+
*/
|
|
9
|
+
export function createOverrideThemeScssEntry({ cssDir, mainScss, themeVar }) {
|
|
10
|
+
const themeFile = path.join(cssDir, `themes/${themeVar}.scss`);
|
|
11
|
+
const tempEntry = path.join(cssDir, `main.${themeVar}.scss`);
|
|
12
|
+
|
|
13
|
+
// Copy main.scss and inject Theme after qld-variables import
|
|
14
|
+
let mainContent = fs.readFileSync(mainScss, "utf8");
|
|
15
|
+
if (fs.existsSync(themeFile)) {
|
|
16
|
+
mainContent = `@import './themes/${themeVar}.scss';` + mainContent;
|
|
17
|
+
} else {
|
|
18
|
+
console.warn(
|
|
19
|
+
`[SCSS Theme] Warning: Theme variables file not found at ${themeFile}`,
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Only write file if it doesn't exist or content has changed
|
|
24
|
+
let shouldWrite = true;
|
|
25
|
+
if (fs.existsSync(tempEntry)) {
|
|
26
|
+
const existingContent = fs.readFileSync(tempEntry, "utf8");
|
|
27
|
+
shouldWrite = existingContent !== mainContent;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (shouldWrite) {
|
|
31
|
+
fs.writeFileSync(tempEntry, mainContent);
|
|
32
|
+
console.log(`[SCSS Theme] Created/updated temp entry: ${tempEntry}`);
|
|
33
|
+
} else {
|
|
34
|
+
console.log(
|
|
35
|
+
`[SCSS Theme] No changes detected, reusing existing: ${tempEntry}`,
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return tempEntry;
|
|
40
|
+
}
|
|
@@ -35,6 +35,10 @@ export default function QGDSupdateHandlebarsPartialsPlugin() {
|
|
|
35
35
|
build.onStart(async () => {
|
|
36
36
|
|
|
37
37
|
const files = getAllFiles(COMPONENTS_DIR);
|
|
38
|
+
// Sort files alphabetically to ensure deterministic ordering
|
|
39
|
+
// This prevents TurboSnap cache invalidation in Chromatic builds
|
|
40
|
+
// caused by non-deterministic filesystem readdir ordering
|
|
41
|
+
files.sort();
|
|
38
42
|
//console.log(files);
|
|
39
43
|
const fileNames = new Map();
|
|
40
44
|
let duplicateFound = false;
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
const loadedThemes = new Map();
|
|
2
|
+
const themeStyleElements = new Map();
|
|
3
|
+
let currentTheme = "masterbrand";
|
|
4
|
+
|
|
5
|
+
// Dynamic theme modules import for lazy loading
|
|
6
|
+
// Automatically generate theme modules based on available theme files
|
|
7
|
+
const themeModules = (() => {
|
|
8
|
+
const modules = {
|
|
9
|
+
customized: () => import("../src/css/main.scss"),
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
// Get all theme files in the themes directory
|
|
13
|
+
const themeContext = import.meta.glob("../src/css/main.*.scss", {
|
|
14
|
+
eager: false,
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
for (const [path, moduleImporter] of Object.entries(themeContext)) {
|
|
18
|
+
// Extract theme name from path (e.g., "../src/css/themes/main.test.scss" -> "test")
|
|
19
|
+
const match = path.match(/(?:main\.)?(\w+)\.scss$/);
|
|
20
|
+
if (match) {
|
|
21
|
+
const themeName = match[1];
|
|
22
|
+
// Only include main.*.scss files or individual theme files if no main.*.scss exists
|
|
23
|
+
if (path.includes(`main.${themeName}.scss`)) {
|
|
24
|
+
modules[themeName] = moduleImporter;
|
|
25
|
+
} else if (
|
|
26
|
+
!Object.keys(themeContext).some((p) =>
|
|
27
|
+
p.includes(`main.${themeName}.scss`),
|
|
28
|
+
)
|
|
29
|
+
) {
|
|
30
|
+
// If no main.*.scss file exists for this theme, use the individual theme file
|
|
31
|
+
modules[themeName] = moduleImporter;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return modules;
|
|
37
|
+
})();
|
|
38
|
+
|
|
39
|
+
function mapStyleElementsByTheme(callback) {
|
|
40
|
+
// Handle both dev mode (style elements) and production mode (link elements)
|
|
41
|
+
const styleElements = document.querySelectorAll('style[type="text/css"]');
|
|
42
|
+
const linkElements = document.querySelectorAll('link[rel="stylesheet"]');
|
|
43
|
+
|
|
44
|
+
styleElements.forEach((element) => {
|
|
45
|
+
callback(element);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
linkElements.forEach((element) => {
|
|
49
|
+
callback(element);
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const unloadTheme = (themeName) => {
|
|
54
|
+
// Cache current theme's style elements before removing
|
|
55
|
+
// Remove existing style elements for current theme
|
|
56
|
+
const currentStyleElements = [];
|
|
57
|
+
const themeNotExist = themeName && !themeStyleElements.has(themeName);
|
|
58
|
+
mapStyleElementsByTheme((element) => {
|
|
59
|
+
if (themeNotExist) {
|
|
60
|
+
currentStyleElements.push(element.cloneNode(true));
|
|
61
|
+
}
|
|
62
|
+
element.remove();
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
if (currentStyleElements.length > 0) {
|
|
66
|
+
themeStyleElements.set(themeName, currentStyleElements);
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const loadTheme = async (themeName) => {
|
|
71
|
+
// Unload current theme if it's different
|
|
72
|
+
if (currentTheme && currentTheme !== themeName) {
|
|
73
|
+
unloadTheme(currentTheme);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// If theme style elements are cached, restore them
|
|
77
|
+
if (themeStyleElements.has(themeName)) {
|
|
78
|
+
const cachedElements = themeStyleElements.get(themeName);
|
|
79
|
+
cachedElements.forEach((element) => {
|
|
80
|
+
document.head.appendChild(element.cloneNode(true));
|
|
81
|
+
});
|
|
82
|
+
currentTheme = themeName;
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// If theme is already loaded, no need to reload
|
|
87
|
+
if (loadedThemes.has(themeName)) {
|
|
88
|
+
currentTheme = themeName;
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const themeModuleImporter = themeModules[themeName];
|
|
93
|
+
if (themeModuleImporter) {
|
|
94
|
+
try {
|
|
95
|
+
// Import the theme module to trigger SCSS loading
|
|
96
|
+
await themeModuleImporter();
|
|
97
|
+
|
|
98
|
+
// Cache the newly created style elements
|
|
99
|
+
const newStyleElements = [];
|
|
100
|
+
|
|
101
|
+
mapStyleElementsByTheme((element) => {
|
|
102
|
+
newStyleElements.push(element.cloneNode(true));
|
|
103
|
+
});
|
|
104
|
+
if (newStyleElements.length > 0) {
|
|
105
|
+
themeStyleElements.set(themeName, newStyleElements);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Store references
|
|
109
|
+
loadedThemes.set(themeName, true);
|
|
110
|
+
currentTheme = themeName;
|
|
111
|
+
} catch (error) {
|
|
112
|
+
console.warn(`Failed to load theme: ${themeName}`, error);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
export const withDynamicTheme = (Story, context) => {
|
|
118
|
+
const { globals } = context;
|
|
119
|
+
const themeName = globals.themeName || "customized";
|
|
120
|
+
|
|
121
|
+
loadTheme(themeName);
|
|
122
|
+
|
|
123
|
+
return Story();
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
export const dynamicThemeGlobalTypes = {
|
|
127
|
+
themeName: {
|
|
128
|
+
name: "Theme Palette",
|
|
129
|
+
description: "Theme palette selector",
|
|
130
|
+
defaultValue: "masterbrand",
|
|
131
|
+
toolbar: {
|
|
132
|
+
icon: "switchalt",
|
|
133
|
+
items: (() => {
|
|
134
|
+
// Dynamically generate toolbar items from available themes
|
|
135
|
+
const items = [];
|
|
136
|
+
|
|
137
|
+
// Add items for all discovered themes
|
|
138
|
+
Object.keys(themeModules).forEach((themeName) => {
|
|
139
|
+
if (themeName !== "customized") {
|
|
140
|
+
const capitalizedName =
|
|
141
|
+
themeName.charAt(0).toUpperCase() + themeName.slice(1);
|
|
142
|
+
items.push({
|
|
143
|
+
value: themeName,
|
|
144
|
+
title: `${capitalizedName} theme`,
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
items.push({ value: "customized", title: "Customized theme" });
|
|
149
|
+
return items;
|
|
150
|
+
})(),
|
|
151
|
+
showName: true,
|
|
152
|
+
dynamicTitle: true,
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
};
|
package/.storybook/main.js
CHANGED
|
@@ -46,6 +46,12 @@ const config = {
|
|
|
46
46
|
|
|
47
47
|
viteFinal: async (config, { configType }) => {
|
|
48
48
|
config.root = "./dist";
|
|
49
|
+
|
|
50
|
+
// Define environment variables for the browser
|
|
51
|
+
config.define = {
|
|
52
|
+
...config.define,
|
|
53
|
+
'import.meta.env.ENABLE_DYNAMIC_THEME': JSON.stringify(process.env.ENABLE_DYNAMIC_THEME === 'true')
|
|
54
|
+
};
|
|
49
55
|
// config.plugins.push({
|
|
50
56
|
// name: "html-transform",
|
|
51
57
|
// transform(src, id) {
|
package/.storybook/preview.js
CHANGED
|
@@ -1,14 +1,32 @@
|
|
|
1
1
|
import "../node_modules/bootstrap/dist/js/bootstrap.bundle.min.js";
|
|
2
2
|
import "../src/js/qld.bootstrap.js";
|
|
3
|
-
import "../src/css/main.scss";
|
|
3
|
+
import "../src/css/main.masterbrand.scss";
|
|
4
4
|
import { withThemeByClassName } from "@storybook/addon-themes";
|
|
5
|
+
import {
|
|
6
|
+
withDynamicTheme,
|
|
7
|
+
dynamicThemeGlobalTypes,
|
|
8
|
+
} from "./dynamicThemeDecorator.js";
|
|
9
|
+
|
|
10
|
+
// Check if dynamic theme should be enabled via environment variable
|
|
11
|
+
const ENABLE_DYNAMIC_THEME = import.meta.env.ENABLE_DYNAMIC_THEME;
|
|
5
12
|
import { allBackgrounds } from "./modes.js";
|
|
6
13
|
import { INITIAL_VIEWPORTS } from "@storybook/addon-viewport";
|
|
7
14
|
import init from "../src/js/handlebars.init.js";
|
|
8
15
|
import Handlebars from "handlebars";
|
|
9
16
|
|
|
17
|
+
// NOTE: TurboSnap Performance Warning
|
|
18
|
+
// The handlebars.init.js import above loads handlebars.partials.js which is
|
|
19
|
+
// auto-generated during build. Changes to this file trigger TurboSnap to
|
|
20
|
+
// rebuild all stories. To prevent false positives:
|
|
21
|
+
// 1. The generator plugin ensures deterministic file ordering
|
|
22
|
+
// 2. The generated file is excluded from linting
|
|
23
|
+
// See: .esbuild/plugins/qgds-plugin-handlebar-partial-builder.js
|
|
24
|
+
|
|
10
25
|
/** @type { import('@storybook/html-vite').Preview } */
|
|
11
26
|
const preview = {
|
|
27
|
+
globalTypes: {
|
|
28
|
+
...(ENABLE_DYNAMIC_THEME ? dynamicThemeGlobalTypes : {}),
|
|
29
|
+
},
|
|
12
30
|
parameters: {
|
|
13
31
|
//actions: { argTypesRegex: "^on[A-Z].*" },
|
|
14
32
|
chromatic: {
|
|
@@ -95,6 +113,7 @@ const preview = {
|
|
|
95
113
|
},
|
|
96
114
|
|
|
97
115
|
decorators: [
|
|
116
|
+
...(ENABLE_DYNAMIC_THEME ? [withDynamicTheme] : []),
|
|
98
117
|
// data-bs-theme="dark" won't be used
|
|
99
118
|
withThemeByClassName({
|
|
100
119
|
themes: {
|
package/README.md
CHANGED
|
@@ -87,25 +87,33 @@ mvn install
|
|
|
87
87
|
```
|
|
88
88
|
|
|
89
89
|
4. Build the Design System CSS, Components and templates
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
npm run build
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
5. Build the same command as above, but with `:theme` following by args `${ThemePaletteName}`
|
|
96
|
+
This will generate a separate altertive CSS export named as `qld.${ThemePaletteName}.bootstrap.css`
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
npm run build:theme ${ThemePaletteName}
|
|
100
|
+
|
|
101
|
+
npm run build:theme -- --theme=${ThemePaletteA} --theme=${ThemePaletteB}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
6. Start Watch and Storybook for component development
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
npm run dev-storybook
|
|
108
|
+
```
|
|
109
|
+
Alt:
|
|
110
|
+
```bash
|
|
111
|
+
npm run watch
|
|
112
|
+
npm run storybook
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
7. Lint
|
|
90
116
|
|
|
91
|
-
```bash
|
|
92
|
-
npm run build
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
5. Start Watch and Storybook for component development
|
|
96
|
-
|
|
97
|
-
```bash
|
|
98
|
-
npm run dev-storybook
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
Alt:
|
|
102
|
-
|
|
103
|
-
```bash
|
|
104
|
-
npm run watch
|
|
105
|
-
npm run storybook
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
6. Lint
|
|
109
117
|
```bash
|
|
110
118
|
npm run lint
|
|
111
119
|
```
|
package/dist/README.md
CHANGED
|
@@ -87,25 +87,33 @@ mvn install
|
|
|
87
87
|
```
|
|
88
88
|
|
|
89
89
|
4. Build the Design System CSS, Components and templates
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
npm run build
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
5. Build the same command as above, but with `:theme` following by args `${ThemePaletteName}`
|
|
96
|
+
This will generate a separate altertive CSS export named as `qld.${ThemePaletteName}.bootstrap.css`
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
npm run build:theme ${ThemePaletteName}
|
|
100
|
+
|
|
101
|
+
npm run build:theme -- --theme=${ThemePaletteA} --theme=${ThemePaletteB}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
6. Start Watch and Storybook for component development
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
npm run dev-storybook
|
|
108
|
+
```
|
|
109
|
+
Alt:
|
|
110
|
+
```bash
|
|
111
|
+
npm run watch
|
|
112
|
+
npm run storybook
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
7. Lint
|
|
90
116
|
|
|
91
|
-
```bash
|
|
92
|
-
npm run build
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
5. Start Watch and Storybook for component development
|
|
96
|
-
|
|
97
|
-
```bash
|
|
98
|
-
npm run dev-storybook
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
Alt:
|
|
102
|
-
|
|
103
|
-
```bash
|
|
104
|
-
npm run watch
|
|
105
|
-
npm run storybook
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
6. Lint
|
|
109
117
|
```bash
|
|
110
118
|
npm run lint
|
|
111
119
|
```
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
<!-- VERSION_DETAILS={"project_id":"@qld-gov-au/qgds-bootstrap5","version":"2.0.
|
|
2
|
+
<!-- VERSION_DETAILS={"project_id":"@qld-gov-au/qgds-bootstrap5","version":"2.0.5","branch":"HEAD","tag":"v2.0.5","commit":"7972857bf46e8fa3db8ca361447711262fffc21e","majorVersion":"v2"} -->
|
|
3
3
|
|
|
4
4
|
{{! Select environment, used verbatium if not using predefind key
|
|
5
5
|
cdn := PROD|STAGING|BETA|TEST|DEV|???
|