Merge pull request 'run storybook in nix derivation' (#5589) from hgl-storybook into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/5589
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
{ lib, ... }:
|
||||
{
|
||||
perSystem =
|
||||
{
|
||||
self',
|
||||
pkgs,
|
||||
config,
|
||||
system,
|
||||
...
|
||||
}:
|
||||
{
|
||||
@@ -20,7 +22,6 @@
|
||||
clan-ts-api = config.packages.clan-ts-api;
|
||||
fonts = config.packages.fonts;
|
||||
};
|
||||
|
||||
};
|
||||
# //
|
||||
# todo add darwin support
|
||||
@@ -41,6 +42,11 @@
|
||||
inherit (config.packages) clan-ts-api;
|
||||
};
|
||||
|
||||
checks = config.packages.clan-app.tests;
|
||||
checks =
|
||||
config.packages.clan-app.tests
|
||||
# Sandboxed Darwin nix build can't spawn a headless brwoser
|
||||
// lib.optionalAttrs (!lib.hasSuffix "darwin" system) {
|
||||
inherit (config.packages.clan-app-ui.tests) clan-app-ui-storybook;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -12,11 +12,13 @@
|
||||
fetchzip,
|
||||
process-compose,
|
||||
json2ts,
|
||||
playwright-driver,
|
||||
playwright,
|
||||
luakit,
|
||||
jq,
|
||||
self',
|
||||
}:
|
||||
let
|
||||
RED = "\\033[1;31m";
|
||||
GREEN = "\\033[1;32m";
|
||||
NC = "\\033[0m";
|
||||
|
||||
@@ -108,32 +110,39 @@ mkShell {
|
||||
export PC_CONFIG_FILES="$CLAN_CORE_PATH/pkgs/clan-app/process-compose.yaml"
|
||||
|
||||
echo -e "${GREEN}To launch a qemu VM for testing, run:\n start-vm <number of VMs>${NC}"
|
||||
''
|
||||
+
|
||||
# todo darwin support needs some work
|
||||
(lib.optionalString stdenv.hostPlatform.isLinux ''
|
||||
# configure playwright for storybook snapshot testing
|
||||
# we only want webkit as that matches what the app is rendered with
|
||||
|
||||
# configure playwright for storybook snapshot testing
|
||||
# we only want webkit as that matches what the app is rendered with
|
||||
|
||||
|
||||
playwright_ver=$(${jq}/bin/jq --raw-output .devDependencies.playwright ${./ui/package.json})
|
||||
if [[ $playwright_ver != '${playwright.version}' ]]; then
|
||||
echo >&2 -en '${RED}'
|
||||
echo >&2 "Error: playwright npm package version ($playwright_ver) is different from that from the nixpkgs (${playwright.version})"
|
||||
echo >&2 "Run this command to update the npm package version"
|
||||
echo >&2
|
||||
echo >&2 " npm i -D --save-exact playwright@${playwright.version}"
|
||||
echo >&2
|
||||
echo >&2 -en '${NC}'
|
||||
else
|
||||
export PLAYWRIGHT_BROWSERS_PATH=${
|
||||
playwright-driver.browsers.override {
|
||||
playwright.browsers.override {
|
||||
withFfmpeg = false;
|
||||
withFirefox = false;
|
||||
withWebkit = true;
|
||||
withFirefox = true;
|
||||
withWebkit = false;
|
||||
withChromium = false;
|
||||
withChromiumHeadlessShell = false;
|
||||
}
|
||||
}
|
||||
|
||||
# This is needed to disable revisionOverrides in browsers.json which
|
||||
# the playwright nix package does not support
|
||||
# https://github.com/NixOS/nixpkgs/blob/f9c3b27aa3f9caac6717973abcc549dbde16bdd4/pkgs/development/web/playwright/driver.nix#L261
|
||||
export PLAYWRIGHT_HOST_PLATFORM_OVERRIDE=nixos
|
||||
|
||||
# stop playwright from trying to validate it has downloaded the necessary browsers
|
||||
# we are providing them manually via nix
|
||||
|
||||
export PLAYWRIGHT_SKIP_VALIDATE_HOST_REQUIREMENTS=true
|
||||
|
||||
# playwright browser drivers are versioned e.g. webkit-2191
|
||||
# this helps us avoid having to update the playwright js dependency everytime we update nixpkgs and vice versa
|
||||
# see vitest.config.js for corresponding launch configuration
|
||||
|
||||
export PLAYWRIGHT_WEBKIT_EXECUTABLE=$(find -L "$PLAYWRIGHT_BROWSERS_PATH" -type f -name "pw_run.sh")
|
||||
'');
|
||||
export PLAYWRIGHT_SKIP_VALIDATE_HOST_REQUIREMENTS=1
|
||||
fi
|
||||
'';
|
||||
}
|
||||
|
||||
@@ -4,8 +4,11 @@
|
||||
importNpmLock,
|
||||
clan-ts-api,
|
||||
fonts,
|
||||
ps,
|
||||
jq,
|
||||
playwright,
|
||||
}:
|
||||
buildNpmPackage (_finalAttrs: {
|
||||
buildNpmPackage (finalAttrs: {
|
||||
pname = "clan-app-ui";
|
||||
version = "0.0.1";
|
||||
nodejs = nodejs_22;
|
||||
@@ -32,36 +35,53 @@ buildNpmPackage (_finalAttrs: {
|
||||
# todo figure out why this fails only inside of Nix
|
||||
# Something about passing orientation in any of the Form stories is causing the browser to crash
|
||||
# `npm run test-storybook-static` works fine in the devshell
|
||||
#
|
||||
# passthru = rec {
|
||||
# storybook = buildNpmPackage {
|
||||
# pname = "${finalAttrs.pname}-storybook";
|
||||
# inherit (finalAttrs)
|
||||
# version
|
||||
# nodejs
|
||||
# src
|
||||
# npmDeps
|
||||
# npmConfigHook
|
||||
# preBuild
|
||||
# ;
|
||||
#
|
||||
# nativeBuildInputs = finalAttrs.nativeBuildInputs ++ [
|
||||
# ps
|
||||
# ];
|
||||
#
|
||||
# npmBuildScript = "test-storybook-static";
|
||||
#
|
||||
# env = finalAttrs.env // {
|
||||
# PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD = 1;
|
||||
# PLAYWRIGHT_BROWSERS_PATH = "${playwright-driver.browsers.override {
|
||||
# withChromiumHeadlessShell = true;
|
||||
# }}";
|
||||
# PLAYWRIGHT_HOST_PLATFORM_OVERRIDE = "ubuntu-24.04";
|
||||
# };
|
||||
#
|
||||
# postBuild = ''
|
||||
# mv storybook-static $out
|
||||
# '';
|
||||
# };
|
||||
# };
|
||||
|
||||
passthru = {
|
||||
tests = {
|
||||
"${finalAttrs.pname}-storybook" = buildNpmPackage {
|
||||
pname = "${finalAttrs.pname}-storybook";
|
||||
inherit (finalAttrs)
|
||||
version
|
||||
nodejs
|
||||
src
|
||||
npmDeps
|
||||
npmConfigHook
|
||||
;
|
||||
|
||||
nativeBuildInputs = finalAttrs.nativeBuildInputs ++ [
|
||||
ps
|
||||
jq
|
||||
];
|
||||
|
||||
npmBuildScript = "test-storybook";
|
||||
|
||||
env = {
|
||||
PLAYWRIGHT_BROWSERS_PATH = "${playwright.browsers.override {
|
||||
withFfmpeg = false;
|
||||
withFirefox = true;
|
||||
withWebkit = false;
|
||||
withChromium = false;
|
||||
withChromiumHeadlessShell = false;
|
||||
}}";
|
||||
PLAYWRIGHT_SKIP_VALIDATE_HOST_REQUIREMENTS = true;
|
||||
# This is needed to disable revisionOverrides in browsers.json which
|
||||
# the playwright nix package does not support:
|
||||
# https://github.com/NixOS/nixpkgs/blob/f9c3b27aa3f9caac6717973abcc549dbde16bdd4/pkgs/development/web/playwright/driver.nix#L261
|
||||
PLAYWRIGHT_HOST_PLATFORM_OVERRIDE = "nixos";
|
||||
DEBUG = "vitest:*";
|
||||
};
|
||||
preBuild = finalAttrs.preBuild + ''
|
||||
playwright_ver=$(jq --raw-output .devDependencies.playwright ${./ui/package.json})
|
||||
if [[ $playwright_ver != '${playwright.version}' ]]; then
|
||||
echo >&2 "playwright npm package version ($playwright_ver) is different from that from the nixpkgs (${playwright.version})"
|
||||
echo >&2 "Run this command to update the npm package version"
|
||||
echo >&2
|
||||
echo >&2 " npm i -D --save-exact playwright@${playwright.version}"
|
||||
echo >&2
|
||||
exit 1
|
||||
fi
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
})
|
||||
|
||||
16
pkgs/clan-app/ui/package-lock.json
generated
16
pkgs/clan-app/ui/package-lock.json
generated
@@ -55,7 +55,7 @@
|
||||
"jsdom": "^26.1.0",
|
||||
"knip": "^5.61.2",
|
||||
"markdown-to-jsx": "^7.7.10",
|
||||
"playwright": "~1.55.1",
|
||||
"playwright": "1.54.1",
|
||||
"postcss": "^8.4.38",
|
||||
"postcss-url": "^10.1.3",
|
||||
"prettier": "^3.2.5",
|
||||
@@ -7292,13 +7292,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/playwright": {
|
||||
"version": "1.55.1",
|
||||
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.55.1.tgz",
|
||||
"integrity": "sha512-cJW4Xd/G3v5ovXtJJ52MAOclqeac9S/aGGgRzLabuF8TnIb6xHvMzKIa6JmrRzUkeXJgfL1MhukP0NK6l39h3A==",
|
||||
"version": "1.54.1",
|
||||
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.54.1.tgz",
|
||||
"integrity": "sha512-peWpSwIBmSLi6aW2auvrUtf2DqY16YYcCMO8rTVx486jKmDTJg7UAhyrraP98GB8BoPURZP8+nxO7TSd4cPr5g==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"playwright-core": "1.55.1"
|
||||
"playwright-core": "1.54.1"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
@@ -7311,9 +7311,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/playwright-core": {
|
||||
"version": "1.55.1",
|
||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.55.1.tgz",
|
||||
"integrity": "sha512-Z6Mh9mkwX+zxSlHqdr5AOcJnfp+xUWLCt9uKV18fhzA8eyxUd8NUWzAjxUh55RZKSYwDGX0cfaySdhZJGMoJ+w==",
|
||||
"version": "1.54.1",
|
||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.54.1.tgz",
|
||||
"integrity": "sha512-Nbjs2zjj0htNhzgiy5wu+3w09YetDx5pkrpI/kZotDlDUaYk0HVA5xrBVPdow4SAUIlhgKcJeJg4GRKW6xHusA==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
|
||||
@@ -14,11 +14,9 @@
|
||||
"vite": "vite",
|
||||
"storybook": "storybook",
|
||||
"knip": "knip --fix",
|
||||
"storybook-build": "storybook build",
|
||||
"storybook-dev": "storybook dev -p 6006",
|
||||
"test-storybook": "vitest run --project storybook",
|
||||
"test-storybook-update-snapshots": "vitest run --project storybook --update",
|
||||
"test-storybook-static": "npm run storybook-build && concurrently -k -s first -n 'SB,TEST' -c 'magenta,blue' 'http-server storybook-static --port 6006 --silent' 'wait-on tcp:127.0.0.1:6006 && npm run test-storybook'"
|
||||
"test-storybook-update-snapshots": "vitest run --project storybook --update"
|
||||
},
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
@@ -50,7 +48,7 @@
|
||||
"jsdom": "^26.1.0",
|
||||
"knip": "^5.61.2",
|
||||
"markdown-to-jsx": "^7.7.10",
|
||||
"playwright": "~1.55.1",
|
||||
"playwright": "1.54.1",
|
||||
"postcss": "^8.4.38",
|
||||
"postcss-url": "^10.1.3",
|
||||
"prettier": "^3.2.5",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { Meta, StoryObj } from "@kachurun/storybook-solid";
|
||||
import { Button, ButtonProps } from "./Button";
|
||||
import { Component } from "solid-js";
|
||||
import { expect, fn, waitFor, within } from "storybook/test";
|
||||
import { expect, fn, within } from "storybook/test";
|
||||
import { StoryContext } from "@kachurun/storybook-solid-vite";
|
||||
|
||||
const getCursorStyle = (el: Element) => window.getComputedStyle(el).cursor;
|
||||
|
||||
@@ -11,6 +11,59 @@ import { Button } from "../Button/Button";
|
||||
const meta: Meta<ModalProps> = {
|
||||
title: "Components/Modal",
|
||||
component: Modal,
|
||||
render: (args: ModalProps) => (
|
||||
<Modal
|
||||
{...args}
|
||||
children={
|
||||
<form class="flex flex-col gap-5">
|
||||
<Fieldset legend="General">
|
||||
{(props: FieldsetFieldProps) => (
|
||||
<>
|
||||
<TextInput
|
||||
{...props}
|
||||
label="First Name"
|
||||
size="s"
|
||||
required={true}
|
||||
input={{ placeholder: "Ron" }}
|
||||
/>
|
||||
<TextInput
|
||||
{...props}
|
||||
label="Last Name"
|
||||
size="s"
|
||||
required={true}
|
||||
input={{ placeholder: "Burgundy" }}
|
||||
/>
|
||||
<TextArea
|
||||
{...props}
|
||||
label="Bio"
|
||||
size="s"
|
||||
input={{
|
||||
placeholder: "Tell us a bit about yourself",
|
||||
rows: 8,
|
||||
}}
|
||||
/>
|
||||
<Checkbox
|
||||
{...props}
|
||||
size="s"
|
||||
label="Accept Terms"
|
||||
required={true}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Fieldset>
|
||||
|
||||
<div class="flex w-full items-center justify-end gap-4">
|
||||
<Button size="s" hierarchy="secondary" onClick={close}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button size="s" type="submit" hierarchy="primary" onClick={close}>
|
||||
Save
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
}
|
||||
/>
|
||||
),
|
||||
};
|
||||
|
||||
export default meta;
|
||||
@@ -21,50 +74,5 @@ export const Default: Story = {
|
||||
args: {
|
||||
title: "Example Modal",
|
||||
onClose: fn(),
|
||||
children: (
|
||||
<form class="flex flex-col gap-5">
|
||||
<Fieldset legend="General">
|
||||
{(props: FieldsetFieldProps) => (
|
||||
<>
|
||||
<TextInput
|
||||
{...props}
|
||||
label="First Name"
|
||||
size="s"
|
||||
required={true}
|
||||
input={{ placeholder: "Ron" }}
|
||||
/>
|
||||
<TextInput
|
||||
{...props}
|
||||
label="Last Name"
|
||||
size="s"
|
||||
required={true}
|
||||
input={{ placeholder: "Burgundy" }}
|
||||
/>
|
||||
<TextArea
|
||||
{...props}
|
||||
label="Bio"
|
||||
size="s"
|
||||
input={{ placeholder: "Tell us a bit about yourself", rows: 8 }}
|
||||
/>
|
||||
<Checkbox
|
||||
{...props}
|
||||
size="s"
|
||||
label="Accept Terms"
|
||||
required={true}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Fieldset>
|
||||
|
||||
<div class="flex w-full items-center justify-end gap-4">
|
||||
<Button size="s" hierarchy="secondary" onClick={close}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button size="s" type="submit" hierarchy="primary" onClick={close}>
|
||||
Save
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
),
|
||||
},
|
||||
};
|
||||
|
||||
@@ -7,11 +7,9 @@ import {
|
||||
} from "@solidjs/router";
|
||||
import { Sidebar } from "@/src/components/Sidebar/Sidebar";
|
||||
import { Suspense } from "solid-js";
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/solid-query";
|
||||
import { addClanURI, resetStore } from "@/src/stores/clan";
|
||||
import { SolidQueryDevtools } from "@tanstack/solid-query-devtools";
|
||||
import { encodeBase64 } from "@/src/hooks/clan";
|
||||
import { ApiClientProvider } from "@/src/hooks/ApiClient";
|
||||
import {
|
||||
ApiCall,
|
||||
OperationArgs,
|
||||
|
||||
@@ -14,6 +14,7 @@ import { splitProps } from "solid-js";
|
||||
import { Typography } from "@/src/components/Typography/Typography";
|
||||
import { MachineTags } from "@/src/components/Form/MachineTags";
|
||||
import { setValue } from "@modular-forms/solid";
|
||||
import { StoryContext } from "@kachurun/storybook-solid-vite";
|
||||
|
||||
type Story = StoryObj<SidebarPaneProps>;
|
||||
|
||||
@@ -30,6 +31,13 @@ const profiles = {
|
||||
const meta: Meta<SidebarPaneProps> = {
|
||||
title: "Components/SidebarPane",
|
||||
component: SidebarPane,
|
||||
decorators: [
|
||||
(
|
||||
Story: StoryObj<SidebarPaneProps>,
|
||||
context: StoryContext<SidebarPaneProps>,
|
||||
) =>
|
||||
() => <Story {...context.args} />,
|
||||
],
|
||||
};
|
||||
|
||||
export default meta;
|
||||
@@ -40,133 +48,140 @@ export const Default: Story = {
|
||||
onClose: () => {
|
||||
console.log("closing");
|
||||
},
|
||||
children: (
|
||||
<>
|
||||
<SidebarSectionForm
|
||||
title="General"
|
||||
schema={v.object({
|
||||
firstName: v.pipe(
|
||||
v.string(),
|
||||
v.nonEmpty("Please enter a first name."),
|
||||
),
|
||||
lastName: v.pipe(
|
||||
v.string(),
|
||||
v.nonEmpty("Please enter a last name."),
|
||||
),
|
||||
bio: v.string(),
|
||||
shareProfile: v.optional(v.boolean()),
|
||||
})}
|
||||
initialValues={profiles.ron}
|
||||
onSubmit={async () => {
|
||||
console.log("saving general");
|
||||
}}
|
||||
>
|
||||
{({ editing, Field }) => (
|
||||
<div class="flex flex-col gap-3">
|
||||
<Field name="firstName">
|
||||
{(field, input) => (
|
||||
<TextInput
|
||||
{...field}
|
||||
size="s"
|
||||
inverted
|
||||
label="First Name"
|
||||
value={field.value}
|
||||
required
|
||||
readOnly={!editing}
|
||||
orientation="horizontal"
|
||||
input={input}
|
||||
/>
|
||||
)}
|
||||
</Field>
|
||||
<Divider />
|
||||
<Field name="lastName">
|
||||
{(field, input) => (
|
||||
<TextInput
|
||||
{...field}
|
||||
size="s"
|
||||
inverted
|
||||
label="Last Name"
|
||||
value={field.value}
|
||||
required
|
||||
readOnly={!editing}
|
||||
orientation="horizontal"
|
||||
input={input}
|
||||
/>
|
||||
)}
|
||||
</Field>
|
||||
<Divider />
|
||||
<Field name="bio">
|
||||
{(field, input) => (
|
||||
<TextArea
|
||||
{...field}
|
||||
value={field.value}
|
||||
size="s"
|
||||
label="Bio"
|
||||
inverted
|
||||
readOnly={!editing}
|
||||
orientation="horizontal"
|
||||
input={{ ...input, rows: 4 }}
|
||||
/>
|
||||
)}
|
||||
</Field>
|
||||
<Field name="shareProfile" type="boolean">
|
||||
{(field, input) => {
|
||||
return (
|
||||
<Checkbox
|
||||
{...splitProps(field, ["value"])[1]}
|
||||
defaultChecked={field.value}
|
||||
},
|
||||
// We have to provide children within a custom render function to ensure we aren't creating any reactivity outside the
|
||||
// solid-js scope.
|
||||
render: (args: SidebarPaneProps) => (
|
||||
<SidebarPane
|
||||
{...args}
|
||||
children={
|
||||
<>
|
||||
<SidebarSectionForm
|
||||
title="General"
|
||||
schema={v.object({
|
||||
firstName: v.pipe(
|
||||
v.string(),
|
||||
v.nonEmpty("Please enter a first name."),
|
||||
),
|
||||
lastName: v.pipe(
|
||||
v.string(),
|
||||
v.nonEmpty("Please enter a last name."),
|
||||
),
|
||||
bio: v.string(),
|
||||
shareProfile: v.optional(v.boolean()),
|
||||
})}
|
||||
initialValues={profiles.ron}
|
||||
onSubmit={async () => {
|
||||
console.log("saving general");
|
||||
}}
|
||||
>
|
||||
{({ editing, Field }) => (
|
||||
<div class="flex flex-col gap-3">
|
||||
<Field name="firstName">
|
||||
{(field, input) => (
|
||||
<TextInput
|
||||
{...field}
|
||||
size="s"
|
||||
label="Share"
|
||||
inverted
|
||||
label="First Name"
|
||||
value={field.value}
|
||||
required
|
||||
readOnly={!editing}
|
||||
orientation="horizontal"
|
||||
input={input}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
</Field>
|
||||
</div>
|
||||
)}
|
||||
</SidebarSectionForm>
|
||||
<SidebarSectionForm
|
||||
title="Tags"
|
||||
schema={v.object({
|
||||
tags: v.pipe(v.array(v.string()), v.nonEmpty()),
|
||||
})}
|
||||
initialValues={profiles.ron}
|
||||
onSubmit={async (values) => {
|
||||
console.log("saving tags", values);
|
||||
}}
|
||||
>
|
||||
{({ editing, Field, formStore }) => (
|
||||
<Field name="tags" type="string[]">
|
||||
{(field, props) => (
|
||||
<MachineTags
|
||||
{...splitProps(field, ["value"])[1]}
|
||||
size="s"
|
||||
onChange={(newVal) => {
|
||||
// Workaround for now, until we manage to use native events
|
||||
setValue(formStore, field.name, newVal);
|
||||
)}
|
||||
</Field>
|
||||
<Divider />
|
||||
<Field name="lastName">
|
||||
{(field, input) => (
|
||||
<TextInput
|
||||
{...field}
|
||||
size="s"
|
||||
inverted
|
||||
label="Last Name"
|
||||
value={field.value}
|
||||
required
|
||||
readOnly={!editing}
|
||||
orientation="horizontal"
|
||||
input={input}
|
||||
/>
|
||||
)}
|
||||
</Field>
|
||||
<Divider />
|
||||
<Field name="bio">
|
||||
{(field, input) => (
|
||||
<TextArea
|
||||
{...field}
|
||||
value={field.value}
|
||||
size="s"
|
||||
label="Bio"
|
||||
inverted
|
||||
readOnly={!editing}
|
||||
orientation="horizontal"
|
||||
input={{ ...input, rows: 4 }}
|
||||
/>
|
||||
)}
|
||||
</Field>
|
||||
<Field name="shareProfile" type="boolean">
|
||||
{(field, input) => {
|
||||
return (
|
||||
<Checkbox
|
||||
{...splitProps(field, ["value"])[1]}
|
||||
defaultChecked={field.value}
|
||||
size="s"
|
||||
label="Share"
|
||||
inverted
|
||||
readOnly={!editing}
|
||||
orientation="horizontal"
|
||||
input={input}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
inverted
|
||||
required
|
||||
readOnly={!editing}
|
||||
orientation="horizontal"
|
||||
defaultValue={field.value}
|
||||
/>
|
||||
)}
|
||||
</Field>
|
||||
)}
|
||||
</SidebarSectionForm>
|
||||
<SidebarSection title="Simple">
|
||||
<Typography tag="h2" hierarchy="title" size="m" inverted>
|
||||
Static Content
|
||||
</Typography>
|
||||
<Typography hierarchy="label" size="s" inverted>
|
||||
This is a non-form section with static content
|
||||
</Typography>
|
||||
</SidebarSection>
|
||||
</>
|
||||
),
|
||||
},
|
||||
</Field>
|
||||
</div>
|
||||
)}
|
||||
</SidebarSectionForm>
|
||||
<SidebarSectionForm
|
||||
title="Tags"
|
||||
schema={v.object({
|
||||
tags: v.pipe(v.array(v.string()), v.nonEmpty()),
|
||||
})}
|
||||
initialValues={profiles.ron}
|
||||
onSubmit={async (values) => {
|
||||
console.log("saving tags", values);
|
||||
}}
|
||||
>
|
||||
{({ editing, Field, formStore }) => (
|
||||
<Field name="tags" type="string[]">
|
||||
{(field, props) => (
|
||||
<MachineTags
|
||||
{...splitProps(field, ["value"])[1]}
|
||||
size="s"
|
||||
onChange={(newVal) => {
|
||||
// Workaround for now, until we manage to use native events
|
||||
setValue(formStore, field.name, newVal);
|
||||
}}
|
||||
inverted
|
||||
required
|
||||
readOnly={!editing}
|
||||
orientation="horizontal"
|
||||
defaultValue={field.value}
|
||||
/>
|
||||
)}
|
||||
</Field>
|
||||
)}
|
||||
</SidebarSectionForm>
|
||||
<SidebarSection title="Simple">
|
||||
<Typography tag="h2" hierarchy="title" size="m" inverted>
|
||||
Static Content
|
||||
</Typography>
|
||||
<Typography hierarchy="label" size="s" inverted>
|
||||
This is a non-form section with static content
|
||||
</Typography>
|
||||
</SidebarSection>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
),
|
||||
};
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Meta, StoryObj } from "@kachurun/storybook-solid";
|
||||
import { Toolbar, ToolbarProps } from "@/src/components/Toolbar/Toolbar";
|
||||
import { Divider } from "@/src/components/Divider/Divider";
|
||||
import { ToolbarButton } from "./ToolbarButton";
|
||||
|
||||
const meta: Meta<ToolbarProps> = {
|
||||
@@ -13,61 +12,35 @@ export default meta;
|
||||
type Story = StoryObj<ToolbarProps>;
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
children: (
|
||||
<>
|
||||
<ToolbarButton
|
||||
name="select"
|
||||
icon="Cursor"
|
||||
description="Select my thing"
|
||||
/>
|
||||
<ToolbarButton
|
||||
name="new-machine"
|
||||
icon="NewMachine"
|
||||
description="Select this thing"
|
||||
/>
|
||||
<Divider orientation="vertical" />
|
||||
<ToolbarButton
|
||||
name="modules"
|
||||
icon="Modules"
|
||||
selected={true}
|
||||
description="Add service"
|
||||
/>
|
||||
<ToolbarButton name="ai" icon="AI" description="Call your AI Manager" />
|
||||
</>
|
||||
),
|
||||
},
|
||||
};
|
||||
|
||||
export const WithTooltip: Story = {
|
||||
// We have to specify children inside a render function to avoid issues with reactivity outside a solid-js context.
|
||||
// @ts-expect-error: args in storybook is not typed correctly. This is a storybook issue.
|
||||
render: (args) => (
|
||||
<div class="flex h-[80vh]">
|
||||
<div class="mt-auto">
|
||||
<Toolbar {...args} />
|
||||
<Toolbar
|
||||
{...args}
|
||||
children={
|
||||
<>
|
||||
<ToolbarButton name="select" icon="Cursor" description="Select" />
|
||||
|
||||
<ToolbarButton
|
||||
name="new-machine"
|
||||
icon="NewMachine"
|
||||
description="Select"
|
||||
/>
|
||||
|
||||
<ToolbarButton
|
||||
name="modules"
|
||||
icon="Modules"
|
||||
selected={true}
|
||||
description="Select"
|
||||
/>
|
||||
|
||||
<ToolbarButton name="ai" icon="AI" description="Select" />
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
args: {
|
||||
children: (
|
||||
<>
|
||||
<ToolbarButton name="select" icon="Cursor" description="Select" />
|
||||
|
||||
<ToolbarButton
|
||||
name="new-machine"
|
||||
icon="NewMachine"
|
||||
description="Select"
|
||||
/>
|
||||
|
||||
<ToolbarButton
|
||||
name="modules"
|
||||
icon="Modules"
|
||||
selected={true}
|
||||
description="Select"
|
||||
/>
|
||||
|
||||
<ToolbarButton name="ai" icon="AI" description="Select" />
|
||||
</>
|
||||
),
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Meta, StoryObj } from "@kachurun/storybook-solid";
|
||||
import { Tooltip, TooltipProps } from "@/src/components/Tooltip/Tooltip";
|
||||
import { Typography } from "@/src/components/Typography/Typography";
|
||||
import { Button } from "@/src/components/Button/Button";
|
||||
|
||||
const meta: Meta<TooltipProps> = {
|
||||
title: "Components/Tooltip",
|
||||
@@ -13,6 +12,23 @@ const meta: Meta<TooltipProps> = {
|
||||
</div>
|
||||
),
|
||||
],
|
||||
render: (args: TooltipProps) => (
|
||||
<div class="p-16">
|
||||
<Tooltip
|
||||
{...args}
|
||||
children={
|
||||
<Typography
|
||||
hierarchy="body"
|
||||
size="xs"
|
||||
inverted={true}
|
||||
weight="medium"
|
||||
>
|
||||
Your Clan is being created
|
||||
</Typography>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
),
|
||||
};
|
||||
|
||||
export default meta;
|
||||
@@ -23,12 +39,6 @@ export const Default: Story = {
|
||||
args: {
|
||||
placement: "top",
|
||||
inverted: false,
|
||||
trigger: <Button hierarchy="primary">Trigger</Button>,
|
||||
children: (
|
||||
<Typography hierarchy="body" size="xs" inverted={true} weight="medium">
|
||||
Your Clan is being created
|
||||
</Typography>
|
||||
),
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
import { getStepStore, useStepper } from "@/src/hooks/stepper";
|
||||
import {
|
||||
createForm,
|
||||
getError,
|
||||
SubmitHandler,
|
||||
valiForm,
|
||||
} from "@modular-forms/solid";
|
||||
import { createForm, SubmitHandler, valiForm } from "@modular-forms/solid";
|
||||
import * as v from "valibot";
|
||||
import { InstallSteps, InstallStoreType } from "../InstallMachine";
|
||||
import { Fieldset } from "@/src/components/Form/Fieldset";
|
||||
|
||||
@@ -5,6 +5,8 @@ import solidSvg from "vite-plugin-solid-svg";
|
||||
import { patchCssModules } from "vite-css-modules";
|
||||
import path from "node:path";
|
||||
import { exec } from "child_process";
|
||||
// @ts-expect-error the type is a bit funky, but it's working
|
||||
import { storybookTest } from "@storybook/addon-vitest/vitest-plugin";
|
||||
|
||||
// watch also clan-cli to catch api changes
|
||||
const clanCliDir = path.resolve(__dirname, "../../clan-cli");
|
||||
@@ -12,7 +14,7 @@ const clanCliDir = path.resolve(__dirname, "../../clan-cli");
|
||||
function regenPythonApiOnFileChange() {
|
||||
return {
|
||||
name: "run-python-script-on-change",
|
||||
handleHotUpdate({}) {
|
||||
handleHotUpdate() {
|
||||
exec("reload-python-api.sh", (err, stdout, stderr) => {
|
||||
if (err) {
|
||||
console.error(`reload-python-api.sh error:\n${stderr}`);
|
||||
@@ -67,4 +69,49 @@ export default defineConfig({
|
||||
},
|
||||
// assetsInlineLimit: 0,
|
||||
},
|
||||
test: {
|
||||
projects: [
|
||||
{
|
||||
test: {
|
||||
name: "unit",
|
||||
},
|
||||
},
|
||||
{
|
||||
extends: path.resolve(__dirname, "vite.config.ts"),
|
||||
plugins: [
|
||||
// The plugin will run tests for the stories defined in your Storybook config
|
||||
// See options at: https://storybook.js.org/docs/next/writing-tests/integrations/vitest-addon#storybooktest
|
||||
storybookTest({
|
||||
configDir: path.resolve(__dirname, ".storybook"),
|
||||
}),
|
||||
],
|
||||
test: {
|
||||
name: "storybook",
|
||||
browser: {
|
||||
// Enable browser-based testing for UI components
|
||||
enabled: true,
|
||||
headless: true,
|
||||
provider: "playwright",
|
||||
instances: [
|
||||
{
|
||||
// Ideally we should use webkit to match clan-app, but inside a
|
||||
// sandboxed nix build, webkit takes forever to finish
|
||||
// launching. Chromium randomly closes itself during testing, as
|
||||
// reported here:
|
||||
// https://github.com/vitest-dev/vitest/discussions/7981
|
||||
//
|
||||
// Firefox is the only browser that can reliably finish the
|
||||
// tests. We want to test storybook only, and the differences
|
||||
// between browsers are probably neglegible to us
|
||||
browser: "firefox",
|
||||
},
|
||||
],
|
||||
},
|
||||
// This setup file applies Storybook project annotations for Vitest
|
||||
// More info at: https://storybook.js.org/docs/api/portable-stories/portable-stories-vitest#setprojectannotations
|
||||
setupFiles: [".storybook/vitest.setup.ts"],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
import solid from "vite-plugin-solid";
|
||||
import { defineConfig, mergeConfig } from "vitest/config";
|
||||
// @ts-expect-error the type is a bit funky, but it's working
|
||||
import { storybookTest } from "@storybook/addon-vitest/vitest-plugin";
|
||||
|
||||
const dirname =
|
||||
typeof __dirname !== "undefined"
|
||||
? __dirname
|
||||
: path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
import viteConfig from "./vite.config";
|
||||
|
||||
export default mergeConfig(
|
||||
viteConfig,
|
||||
defineConfig({
|
||||
plugins: [solid()],
|
||||
test: {
|
||||
projects: [
|
||||
{
|
||||
test: {
|
||||
name: "unit",
|
||||
},
|
||||
},
|
||||
{
|
||||
extends: path.join(dirname, "vite.config.ts"),
|
||||
plugins: [
|
||||
// The plugin will run tests for the stories defined in your Storybook config
|
||||
// See options at: https://storybook.js.org/docs/next/writing-tests/integrations/vitest-addon#storybooktest
|
||||
storybookTest({
|
||||
configDir: path.join(dirname, ".storybook"),
|
||||
}),
|
||||
],
|
||||
test: {
|
||||
name: "storybook",
|
||||
browser: {
|
||||
// Enable browser-based testing for UI components
|
||||
enabled: true,
|
||||
headless: true,
|
||||
provider: "playwright",
|
||||
instances: [
|
||||
{
|
||||
browser: "webkit",
|
||||
launch: {
|
||||
executablePath: process.env.PLAYWRIGHT_WEBKIT_EXECUTABLE,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
// This setup file applies Storybook project annotations for Vitest
|
||||
// More info at: https://storybook.js.org/docs/api/portable-stories/portable-stories-vitest#setprojectannotations
|
||||
setupFiles: [".storybook/vitest.setup.ts"],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
);
|
||||
Reference in New Issue
Block a user