Skip to content

Commit b68f7ef

Browse files
committed
progress on fs upgrades
1 parent d20c90c commit b68f7ef

65 files changed

Lines changed: 1173 additions & 1906 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.cursor/rules/CODE_STYLE.mdc

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ function getData() {}
180180
```typescript
181181
// ✅ Good - Shades component with event handlers
182182
const MyComponent = Shade({
183-
shadowDomName: 'my-component',
183+
customElementName: 'my-component',
184184
render: ({ props, injector }) => {
185185
const handleButtonClick = () => {
186186
// Internal logic
@@ -267,7 +267,7 @@ type UserProfileProps = {
267267
};
268268

269269
export const UserProfile = Shade<UserProfileProps>({
270-
shadowDomName: 'user-profile',
270+
customElementName: 'user-profile',
271271
render: ({ props }) => {
272272
// Component implementation
273273
},
@@ -501,7 +501,7 @@ const formatDate = (date: Date): string => {
501501

502502
// 5. Main component
503503
export const UserProfile = Shade<UserProfileProps>({
504-
shadowDomName: 'user-profile',
504+
customElementName: 'user-profile',
505505
render: ({ props, injector, useObservable, useDisposable }) => {
506506
// Services
507507
const userService = injector.getInstance(UserService);
@@ -646,7 +646,7 @@ export const formatCurrency = (value: number, currency: string): string => {
646646
* UserProfile component displays user information and allows editing
647647
*/
648648
export const UserProfile = Shade<UserProfileProps>({
649-
shadowDomName: 'user-profile',
649+
customElementName: 'user-profile',
650650
render: ({ props }) => {
651651
// Component implementation
652652
},

.cursor/rules/SHADES_COMPONENTS.mdc

Lines changed: 30 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ type MyComponentProps = {
2424
};
2525

2626
export const MyComponent = Shade<MyComponentProps>({
27-
shadowDomName: 'my-component',
27+
customElementName: 'my-component',
2828
render: ({ props, injector }) => {
2929
return (
3030
<div>
@@ -42,15 +42,15 @@ Always provide a unique `shadowDomName` in kebab-case:
4242

4343
```typescript
4444
// ✅ Good - unique, descriptive kebab-case names
45-
shadowDomName: 'shade-app-layout'
46-
shadowDomName: 'shade-login'
47-
shadowDomName: 'theme-switch'
48-
shadowDomName: 'github-logo'
45+
customElementName: 'shade-app-layout'
46+
customElementName: 'shade-login'
47+
customElementName: 'theme-switch'
48+
customElementName: 'github-logo'
4949

5050
// ❌ Avoid - generic or poorly named
51-
shadowDomName: 'my-component'
52-
shadowDomName: 'div'
53-
shadowDomName: 'Component1'
51+
customElementName: 'my-component'
52+
customElementName: 'div'
53+
customElementName: 'Component1'
5454
```
5555

5656
## Render Function Parameters
@@ -79,11 +79,11 @@ Get service instances using the injector:
7979

8080
```typescript
8181
export const MyComponent = Shade({
82-
shadowDomName: 'my-component',
82+
customElementName: 'my-component',
8383
render: ({ injector }) => {
8484
const themeProvider = injector.getInstance(ThemeProviderService);
8585
const sessionService = injector.getInstance(SessionService);
86-
86+
8787
return (
8888
<div style={{ backgroundColor: themeProvider.theme.background.default }}>
8989
{/* Component content */}
@@ -101,10 +101,10 @@ Use `useState` for component-local state:
101101

102102
```typescript
103103
export const Counter = Shade({
104-
shadowDomName: 'app-counter',
104+
customElementName: 'app-counter',
105105
render: ({ useState }) => {
106106
const [count, setCount] = useState<number>('count', 0);
107-
107+
108108
return (
109109
<div>
110110
<span>Count: {count}</span>
@@ -121,22 +121,22 @@ Subscribe to `ObservableValue` from services:
121121

122122
```typescript
123123
export const UserStatus = Shade({
124-
shadowDomName: 'user-status',
124+
customElementName: 'user-status',
125125
render: ({ injector, useObservable }) => {
126126
const sessionService = injector.getInstance(SessionService);
127-
127+
128128
// Subscribe to observable values
129129
const [isOperationInProgress] = useObservable(
130130
'isOperationInProgress',
131131
sessionService.isOperationInProgress
132132
);
133133
const [currentUser] = useObservable('currentUser', sessionService.currentUser);
134134
const [state] = useObservable('state', sessionService.state);
135-
135+
136136
if (isOperationInProgress) {
137137
return <div>Loading...</div>;
138138
}
139-
139+
140140
return (
141141
<div>
142142
{currentUser ? `Welcome, ${currentUser.username}` : 'Not logged in'}
@@ -154,18 +154,18 @@ Properly manage subscriptions and resources:
154154

155155
```typescript
156156
export const ThemeSwitch = Shade({
157-
shadowDomName: 'theme-switch',
157+
customElementName: 'theme-switch',
158158
render: ({ injector, useState, useDisposable }) => {
159159
const themeProvider = injector.getInstance(ThemeProviderService);
160160
const [theme, setTheme] = useState<'light' | 'dark'>('theme', 'dark');
161-
161+
162162
// Subscribe to theme changes with automatic cleanup
163163
useDisposable('traceThemeChange', () =>
164164
themeProvider.subscribe('themeChanged', (newTheme) => {
165165
setTheme(newTheme.name === 'dark' ? 'dark' : 'light');
166166
}),
167167
);
168-
168+
169169
return <button>{theme === 'dark' ? '☀️' : '🌜'}</button>;
170170
},
171171
});
@@ -188,10 +188,10 @@ import {
188188
} from '@furystack/shades-common-components';
189189

190190
export const LoginForm = Shade({
191-
shadowDomName: 'login-form',
191+
customElementName: 'login-form',
192192
render: ({ injector }) => {
193193
const sessionService = injector.getInstance(SessionService);
194-
194+
195195
return (
196196
<Paper elevation={3}>
197197
<Form<{ username: string; password: string }>
@@ -312,10 +312,10 @@ Use `ThemeProviderService` for consistent theming:
312312

313313
```typescript
314314
export const ThemedComponent = Shade({
315-
shadowDomName: 'themed-component',
315+
customElementName: 'themed-component',
316316
render: ({ injector }) => {
317317
const theme = injector.getInstance(ThemeProviderService).theme;
318-
318+
319319
return (
320320
<div
321321
style={{
@@ -336,13 +336,11 @@ export const ThemedComponent = Shade({
336336
Allow users to switch between themes:
337337

338338
```typescript
339-
import { defaultDarkTheme, defaultLightTheme } from '@furystack/shades-common-components';
339+
import { defaultDarkTheme, defaultLightTheme } from '@furystack/shades-common-components'
340340

341341
const toggleTheme = (themeProvider: ThemeProviderService, currentTheme: 'light' | 'dark') => {
342-
themeProvider.setAssignedTheme(
343-
currentTheme === 'dark' ? defaultLightTheme : defaultDarkTheme
344-
);
345-
};
342+
themeProvider.setAssignedTheme(currentTheme === 'dark' ? defaultLightTheme : defaultDarkTheme)
343+
}
346344
```
347345

348346
## Page Components
@@ -356,7 +354,7 @@ Place page components in `frontend/src/pages/`:
356354
import { createComponent, Shade } from '@furystack/shades';
357355

358356
export const Dashboard = Shade({
359-
shadowDomName: 'page-dashboard',
357+
customElementName: 'page-dashboard',
360358
render: ({ injector }) => {
361359
return (
362360
<div>
@@ -374,9 +372,9 @@ Export pages from an index file:
374372

375373
```typescript
376374
// frontend/src/pages/index.ts
377-
export * from './dashboard.js';
378-
export * from './login.js';
379-
export * from './hello-world.js';
375+
export * from './dashboard.js'
376+
export * from './login.js'
377+
export * from './hello-world.js'
380378
```
381379

382380
## Application Entry Point

.cursor/rules/TYPESCRIPT_GUIDELINES.mdc

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ type ButtonProps = {
2828
};
2929

3030
export const Button = Shade<ButtonProps>({
31-
shadowDomName: 'app-button',
31+
customElementName: 'app-button',
3232
render: ({ props }) => {
3333
const variant = props.variant ?? 'contained';
3434
const color = props.color ?? 'primary';
@@ -51,7 +51,7 @@ export const Button = Shade<ButtonProps>({
5151

5252
// ✅ Good - explicit return type for complex conditional logic
5353
export const StatusBadge = Shade<{ status: string }>({
54-
shadowDomName: 'status-badge',
54+
customElementName: 'status-badge',
5555
render: ({ props }): JSX.Element | null => {
5656
switch (props.status) {
5757
case 'active':
@@ -88,7 +88,7 @@ type UserProfileProps = {
8888
}
8989

9090
export const UserProfile = Shade<UserProfileProps>({
91-
shadowDomName: 'user-profile',
91+
customElementName: 'user-profile',
9292
render: ({ props }) => {
9393
// Component implementation
9494
},
@@ -132,7 +132,7 @@ type ListProps<T> = {
132132
};
133133

134134
export const List = <T,>(props: ListProps<T>) => Shade({
135-
shadowDomName: 'generic-list',
135+
customElementName: 'generic-list',
136136
render: () => {
137137
return (
138138
<ul data-testid="list">
@@ -172,7 +172,7 @@ type DataListProps<T extends DataItem> = {
172172
};
173173

174174
export const DataList = <T extends DataItem>(props: DataListProps<T>) => Shade({
175-
shadowDomName: 'data-list',
175+
customElementName: 'data-list',
176176
render: () => {
177177
return (
178178
<div data-testid="data-list">
@@ -475,7 +475,7 @@ const hasUsers = users.length > 0; // Inferred as boolean
475475

476476
// ✅ Good - infer component return types when straightforward
477477
export const SimpleComponent = Shade<{ title: string }>({
478-
shadowDomName: 'simple-component',
478+
customElementName: 'simple-component',
479479
render: ({ props }) => {
480480
return <div>{props.title}</div>; // Return type inferred
481481
},

.github/workflows/ui-tests.yml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,10 @@ jobs:
2222
run: yarn build
2323
- name: Install Playwright browsers
2424
run: yarn playwright install --with-deps
25-
- name: Seed test data
26-
run: yarn seed
27-
- name: Execute tests
25+
26+
- name: Run installer tests
27+
run: yarn test:e2e:install
28+
- name: Run app tests
2829
run: yarn test:e2e
2930
- uses: actions/upload-artifact@v4
3031
if: failure()

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ vitest.config.mjs.map
8181
service/data.sqlite
8282
service/data/
8383
testresults
84+
test-results
8485

8586
.pnp.*
8687
.yarn/*

azure-pipelines.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,11 @@ steps:
3434
- script: yarn playwright install --with-deps
3535
displayName: Install Playwright browsers
3636

37-
- script: yarn seed
38-
displayName: 'Seed test data'
37+
- script: yarn test:e2e:install
38+
displayName: 'E2E installer tests'
3939

4040
- script: yarn test:e2e
41-
displayName: 'E2E tests'
41+
displayName: 'E2E app tests'
4242

4343
- task: PublishTestResults@2
4444
displayName: Publish test results

common/package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,12 @@
2525
"create-schemas": "node ./dist/bin/create-schemas.js"
2626
},
2727
"devDependencies": {
28-
"@types/node": "^25.3.5",
28+
"@types/node": "^25.5.0",
2929
"ts-json-schema-generator": "^2.9.0",
30-
"vitest": "^4.0.18"
30+
"vitest": "^4.1.2"
3131
},
3232
"dependencies": {
33-
"@furystack/core": "^15.2.4",
34-
"@furystack/rest": "^8.0.42"
33+
"@furystack/core": "^16.0.2",
34+
"@furystack/rest": "^8.1.3"
3535
}
3636
}

e2e/helpers.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import type { Page } from '@playwright/test'
2+
import { expect } from '@playwright/test'
3+
4+
export const login = async (page: Page, userName = 'testuser', password = 'password') => {
5+
const loginForm = page.locator('shade-login form')
6+
await expect(loginForm).toBeVisible({ timeout: 15000 })
7+
8+
await loginForm.locator('input[name="userName"]').fill(userName)
9+
await loginForm.locator('input[name="password"]').fill(password)
10+
await page.locator('button', { hasText: 'Sign in' }).click()
11+
12+
await expect(page.locator('shade-dashboard')).toBeVisible({ timeout: 10000 })
13+
}

e2e/installer.spec.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { expect, test } from '@playwright/test'
2+
3+
test.describe.serial('Installer Wizard', () => {
4+
test('should complete the installer wizard', async ({ page }) => {
5+
await page.goto('/')
6+
7+
const welcomeStep = page.locator('shade-welcome-step')
8+
await expect(welcomeStep).toBeVisible()
9+
10+
const welcomeStepNextButton = welcomeStep.locator('button', { hasText: 'Next' })
11+
12+
await expect(page.locator('text=Welcome to StackCraft')).toBeVisible()
13+
await welcomeStepNextButton.click()
14+
15+
const prerequisitesStep = page.locator('shade-check-prerequisites-step')
16+
await expect(page.locator('text=Prerequisites')).toBeVisible()
17+
const prerequisitesStepNextButton = prerequisitesStep.locator('button', { hasText: 'Next' })
18+
await prerequisitesStepNextButton.click()
19+
20+
const adminStep = page.locator('shade-create-admin-step')
21+
22+
await expect(page.locator('text=Create Admin User')).toBeVisible()
23+
await page.locator('input[name="username"]').fill('testuser')
24+
await page.locator('input[name="password"]').fill('password')
25+
26+
const adminStepNextButton = adminStep.locator('button', { hasText: 'Next' })
27+
await adminStepNextButton.click()
28+
29+
await expect(page.locator('text=Success')).toBeVisible()
30+
await page.locator('button', { hasText: 'Finish' }).click()
31+
})
32+
33+
test('should show login page after install', async ({ page }) => {
34+
await page.goto('/')
35+
36+
const loginForm = page.locator('shade-login form')
37+
await expect(loginForm).toBeVisible()
38+
})
39+
})

0 commit comments

Comments
 (0)