web: simplify ?inline
handler for Storybook (#12246)
* web: Add InvalidationFlow to Radius Provider dialogues ## What - Bugfix: adds the InvalidationFlow to the Radius Provider dialogues - Repairs: `{"invalidation_flow":["This field is required."]}` message, which was *not* propagated to the Notification. - Nitpick: Pretties `?foo=${true}` expressions: `s/\?([^=]+)=\$\{true\}/\1/` ## Note Yes, I know I'm going to have to do more magic when we harmonize the forms, and no, I didn't add the Property Mappings to the wizard, and yes, I know I'm going to have pain with the *new* version of the wizard. But this is a serious bug; you can't make Radius servers with *either* of the current dialogues at the moment. * web: simplify `?inline` handler for Storybook # What - Revise the `?inline` handler for Storybook - Enable headless test runs of E2E - Reduce headless testing to single instances # Why ## `?inline` handling Vite-for-Storybook-for-Web-Components has a requirement that all component CSS imports be suffixed with an `?inline` argument so Vite knows to put the CSS into the component and not inject it into the document head. This `?inline` argument is an implementation detail of Storybook. It would be irrelevant clutter added to our codebase. We were using `rollup-plugin-modify` to find every instance of an import-to-component, but the implementation was clunky and involved scanning the source code manually. `rollup-plugin-modify` version 3 has regular expressions and takes a function as an argument. This allows us to generate the CSS import maps on-the-fly when Storybook is run, eliminating a fragile build step. We can also remove the source code scanner for those imports. ## Changes to testing It's just nice to be able to run the E2E tests headlessly, without them eating up your screen real estate, flashing, or grabbing your mouse. WebdriverIO's testing of Web Components is new and, as we've seen, a bit cranky. The WebdriverIO team currently recommends not running the tests in parallel. We only have about 70 tests so far, and they're fairly speedy, especially when you don't have to invoke a browser session for every test.
This commit is contained in:
@ -1,84 +0,0 @@
|
||||
// THIS IS A GENERATED FILE. DO NOT EDIT BY HAND.
|
||||
//
|
||||
// This file is generated by the build-storybook-import-maps script in the UI's base directory.
|
||||
// This is a *hack* to work around an inconsistency in the way rollup, vite, and storybook
|
||||
// import CSS modules.
|
||||
//
|
||||
// Sometime around 2030 or so, the Javascript community may finally get its collective act together
|
||||
// and we'll have one unified way of doing this. I can only hope.
|
||||
|
||||
const rawCssImportMaps = [
|
||||
'import AKGlobal from "../../../common/styles/authentik.css";',
|
||||
'import AKGlobal from "../../common/styles/authentik.css";',
|
||||
'import AKGlobal from "../common/styles/authentik.css";',
|
||||
'import AKGlobal from "@goauthentik/common/styles/authentik.css";',
|
||||
'import PFAlert from "@patternfly/patternfly/components/Alert/alert.css";',
|
||||
'import PFAlertGroup from "@patternfly/patternfly/components/AlertGroup/alert-group.css";',
|
||||
'import PFAvatar from "@patternfly/patternfly/components/Avatar/avatar.css";',
|
||||
'import PFBackdrop from "@patternfly/patternfly/components/Backdrop/backdrop.css";',
|
||||
'import PFBackgroundImage from "@patternfly/patternfly/components/BackgroundImage/background-image.css";',
|
||||
'import PFBanner from "@patternfly/patternfly/components/Banner/banner.css";',
|
||||
'import PFBase from "@patternfly/patternfly/patternfly-base.css";',
|
||||
'import PFBrand from "@patternfly/patternfly/components/Brand/brand.css";',
|
||||
'import PFBullseye from "@patternfly/patternfly/layouts/Bullseye/bullseye.css";',
|
||||
'import PFButton from "@patternfly/patternfly/components/Button/button.css";',
|
||||
'import PFCard from "@patternfly/patternfly/components/Card/card.css";',
|
||||
'import PFCheck from "@patternfly/patternfly/components/Check/check.css";',
|
||||
'import PFChip from "@patternfly/patternfly/components/Chip/chip.css";',
|
||||
'import PFChipGroup from "@patternfly/patternfly/components/ChipGroup/chip-group.css";',
|
||||
'import PFContent from "@patternfly/patternfly/components/Content/content.css";',
|
||||
'import PFDataList from "@patternfly/patternfly/components/DataList/data-list.css";',
|
||||
'import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList/description-list.css";',
|
||||
'import PFDisplay from "@patternfly/patternfly/utilities/Display/display.css";',
|
||||
'import PFDivider from "@patternfly/patternfly/components/Divider/divider.css";',
|
||||
'import PFDrawer from "@patternfly/patternfly/components/Drawer/drawer.css";',
|
||||
'import PFDropdown from "@patternfly/patternfly/components/Dropdown/dropdown.css";',
|
||||
'import PFDualListSelector from "@patternfly/patternfly/components/DualListSelector/dual-list-selector.css";',
|
||||
'import PFEmptyState from "@patternfly/patternfly/components/EmptyState/empty-state.css";',
|
||||
'import PFExpandableSection from "@patternfly/patternfly/components/ExpandableSection/expandable-section.css";',
|
||||
'import PFFAIcons from "@patternfly/patternfly/base/patternfly-fa-icons.css";',
|
||||
'import PFFlex from "@patternfly/patternfly/layouts/Flex/flex.css";',
|
||||
'import PFForm from "@patternfly/patternfly/components/Form/form.css";',
|
||||
'import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";',
|
||||
'import PFGallery from "@patternfly/patternfly/layouts/Gallery/gallery.css";',
|
||||
'import PFGlobal from "@patternfly/patternfly/patternfly-base.css";',
|
||||
'import PFGrid from "@patternfly/patternfly/layouts/Grid/grid.css";',
|
||||
'import PFInputGroup from "@patternfly/patternfly/components/InputGroup/input-group.css";',
|
||||
'import PFLabel from "@patternfly/patternfly/components/Label/label.css";',
|
||||
'import PFList from "@patternfly/patternfly/components/List/list.css";',
|
||||
'import PFLogin from "@patternfly/patternfly/components/Login/login.css";',
|
||||
'import PFModalBox from "@patternfly/patternfly/components/ModalBox/modal-box.css";',
|
||||
'import PFNav from "@patternfly/patternfly/components/Nav/nav.css";',
|
||||
'import PFNotificationBadge from "@patternfly/patternfly/components/NotificationBadge/notification-badge.css";',
|
||||
'import PFNotificationDrawer from "@patternfly/patternfly/components/NotificationDrawer/notification-drawer.css";',
|
||||
'import PFPage from "@patternfly/patternfly/components/Page/page.css";',
|
||||
'import PFPagination from "@patternfly/patternfly/components/Pagination/pagination.css";',
|
||||
'import PFProgress from "@patternfly/patternfly/components/Progress/progress.css";',
|
||||
'import PFProgressStepper from "@patternfly/patternfly/components/ProgressStepper/progress-stepper.css";',
|
||||
'import PFRadio from "@patternfly/patternfly/components/Radio/radio.css";',
|
||||
'import PFSelect from "@patternfly/patternfly/components/Select/select.css";',
|
||||
'import PFSidebar from "@patternfly/patternfly/components/Sidebar/sidebar.css";',
|
||||
'import PFSizing from "@patternfly/patternfly/utilities/Sizing/sizing.css";',
|
||||
'import PFSpacing from "@patternfly/patternfly/utilities/Spacing/spacing.css";',
|
||||
'import PFSpinner from "@patternfly/patternfly/components/Spinner/spinner.css";',
|
||||
'import PFSplit from "@patternfly/patternfly/layouts/Split/split.css";',
|
||||
'import PFStack from "@patternfly/patternfly/layouts/Stack/stack.css";',
|
||||
'import PFSwitch from "@patternfly/patternfly/components/Switch/switch.css";',
|
||||
'import PFTable from "@patternfly/patternfly/components/Table/table.css";',
|
||||
'import PFTabs from "@patternfly/patternfly/components/Tabs/tabs.css";',
|
||||
'import PFTitle from "@patternfly/patternfly/components/Title/title.css";',
|
||||
'import PFToggleGroup from "@patternfly/patternfly/components/ToggleGroup/toggle-group.css";',
|
||||
'import PFToolbar from "@patternfly/patternfly/components/Toolbar/toolbar.css";',
|
||||
'import PFTreeView from "@patternfly/patternfly/components/TreeView/tree-view.css";',
|
||||
'import PFWizard from "@patternfly/patternfly/components/Wizard/wizard.css";',
|
||||
'import ThemeDark from "@goauthentik/common/styles/theme-dark.css";',
|
||||
'import styles from "./LibraryPageImpl.css";',
|
||||
];
|
||||
|
||||
const cssImportMaps = rawCssImportMaps.reduce(
|
||||
(acc, line) => ({ ...acc, [line]: line.replace(/\.css/, ".css?inline") }),
|
||||
{},
|
||||
);
|
||||
|
||||
export { cssImportMaps };
|
||||
export default cssImportMaps;
|
@ -5,11 +5,19 @@ import modify from "rollup-plugin-modify";
|
||||
import postcssLit from "rollup-plugin-postcss-lit";
|
||||
import tsconfigPaths from "vite-tsconfig-paths";
|
||||
|
||||
import { cssImportMaps } from "./css-import-maps";
|
||||
|
||||
export const isProdBuild = process.env.NODE_ENV === "production";
|
||||
export const apiBasePath = process.env.AK_API_BASE_PATH || "";
|
||||
|
||||
const importInlinePatterns = [
|
||||
'import AKGlobal from "(\\.\\./)*common/styles/authentik\\.css',
|
||||
'import AKGlobal from "@goauthentik/common/styles/authentik\\.css',
|
||||
'import PF.+ from "@patternfly/patternfly/\\S+\\.css',
|
||||
'import ThemeDark from "@goauthentik/common/styles/theme-dark\\.css',
|
||||
'import styles from "\\./LibraryPageImpl\\.css',
|
||||
];
|
||||
|
||||
const importInlineRegexp = new RegExp(importInlinePatterns.map((a) => `(${a})`).join("|"));
|
||||
|
||||
const config: StorybookConfig = {
|
||||
stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"],
|
||||
addons: [
|
||||
@ -43,7 +51,12 @@ const config: StorybookConfig = {
|
||||
return {
|
||||
...config,
|
||||
plugins: [
|
||||
modify(cssImportMaps),
|
||||
modify({
|
||||
find: importInlineRegexp,
|
||||
replace: (match: RegExpMatchArray) => {
|
||||
return `${match}?inline`;
|
||||
},
|
||||
}),
|
||||
replace({
|
||||
"process.env.NODE_ENV": JSON.stringify(
|
||||
isProdBuild ? "production" : "development",
|
||||
|
@ -133,8 +133,8 @@
|
||||
"pseudolocalize": "wireit",
|
||||
"storybook": "storybook dev -p 6006",
|
||||
"storybook:build": "wireit",
|
||||
"storybook:build-import-map": "wireit",
|
||||
"test": "wireit",
|
||||
"test:e2e": "wireit",
|
||||
"test:e2e:watch": "wireit",
|
||||
"test:watch": "wireit",
|
||||
"tsc": "wireit",
|
||||
@ -316,9 +316,6 @@
|
||||
"NODE_OPTIONS": "--max_old_space_size=8192"
|
||||
}
|
||||
},
|
||||
"storybook:build-import-map": {
|
||||
"command": "node scripts/build-storybook-import-maps.mjs"
|
||||
},
|
||||
"test": {
|
||||
"command": "wdio ./wdio.conf.ts --logLevel=warn",
|
||||
"env": {
|
||||
@ -326,6 +323,16 @@
|
||||
"TS_NODE_PROJECT": "tsconfig.test.json"
|
||||
}
|
||||
},
|
||||
"test:e2e": {
|
||||
"command": "wdio run ./tests/wdio.conf.ts",
|
||||
"dependencies": [
|
||||
"build"
|
||||
],
|
||||
"env": {
|
||||
"CI": "true",
|
||||
"TS_NODE_PROJECT": "./tests/tsconfig.test.json"
|
||||
}
|
||||
},
|
||||
"test:e2e:watch": {
|
||||
"command": "wdio run ./tests/wdio.conf.ts",
|
||||
"dependencies": [
|
||||
|
@ -1,91 +0,0 @@
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
|
||||
const __dirname = fileURLToPath(new URL(".", import.meta.url));
|
||||
|
||||
function* walkFilesystem(dir) {
|
||||
const openeddir = fs.opendirSync(dir);
|
||||
if (!openeddir) {
|
||||
return;
|
||||
}
|
||||
let d;
|
||||
while ((d = openeddir?.readSync())) {
|
||||
if (!d) {
|
||||
break;
|
||||
}
|
||||
const entry = path.join(dir, d.name);
|
||||
if (d.isDirectory()) yield* walkFilesystem(entry);
|
||||
else if (d.isFile()) yield entry;
|
||||
}
|
||||
openeddir.close();
|
||||
}
|
||||
|
||||
const import_re = /^(import \w+ from .*\.css)";/;
|
||||
|
||||
function extractImportLinesFromFile(path) {
|
||||
const source = fs.readFileSync(path, { encoding: "utf8", flag: "r" });
|
||||
const lines = source?.split("\n") ?? [];
|
||||
return lines.filter((l) => import_re.test(l));
|
||||
}
|
||||
|
||||
function createOneImportLine(line) {
|
||||
const importMatch = import_re.exec(line);
|
||||
if (!importMatch) {
|
||||
throw new Error("How did an unmatchable line get here?");
|
||||
}
|
||||
const importContent = importMatch[1];
|
||||
if (!importContent) {
|
||||
throw new Error("How did an unmatchable line get here!?");
|
||||
}
|
||||
return ` '${importContent}";',`;
|
||||
}
|
||||
|
||||
const isSourceFile = /\.ts$/;
|
||||
|
||||
function getTheSourceFiles() {
|
||||
return Array.from(walkFilesystem(path.join(__dirname, "..", "src"))).filter((path) =>
|
||||
isSourceFile.test(path),
|
||||
);
|
||||
}
|
||||
|
||||
function getTheImportLines(importPaths) {
|
||||
const importLines = importPaths.reduce(
|
||||
(acc, path) => [...acc, extractImportLinesFromFile(path)].flat(),
|
||||
[],
|
||||
);
|
||||
const uniqueImportLines = new Set(importLines);
|
||||
const sortedImportLines = Array.from(uniqueImportLines.keys());
|
||||
sortedImportLines.sort();
|
||||
return sortedImportLines;
|
||||
}
|
||||
|
||||
const importPaths = getTheSourceFiles();
|
||||
const importLines = getTheImportLines(importPaths);
|
||||
|
||||
const outputFile = `// THIS IS A GENERATED FILE. DO NOT EDIT BY HAND.
|
||||
//
|
||||
// This file is generated by the build-storybook-import-maps script in the UI's base directory.
|
||||
// This is a *hack* to work around an inconsistency in the way rollup, vite, and storybook
|
||||
// import CSS modules.
|
||||
//
|
||||
// Sometime around 2030 or so, the Javascript community may finally get its collective act together
|
||||
// and we'll have one unified way of doing this. I can only hope.
|
||||
|
||||
const rawCssImportMaps = [
|
||||
${importLines.map(createOneImportLine).join("\n")}
|
||||
];
|
||||
|
||||
const cssImportMaps = rawCssImportMaps.reduce(
|
||||
(acc, line) => ({ ...acc, [line]: line.replace(/\\.css/, ".css?inline") }),
|
||||
{},
|
||||
);
|
||||
|
||||
export { cssImportMaps };
|
||||
export default cssImportMaps;
|
||||
`;
|
||||
|
||||
fs.writeFileSync(path.join(__dirname, "..", ".storybook", "css-import-maps.ts"), outputFile, {
|
||||
encoding: "utf8",
|
||||
flag: "w",
|
||||
});
|
@ -59,7 +59,7 @@ const maxInstances =
|
||||
process.env.MAX_INSTANCES !== undefined
|
||||
? parseInt(process.env.MAX_INSTANCES, DEFAULT_MAX_INSTANCES)
|
||||
: runHeadless
|
||||
? 10
|
||||
? 1
|
||||
: 1;
|
||||
|
||||
export const config: WebdriverIO.Config = {
|
||||
|
Reference in New Issue
Block a user