Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 74 additions & 8 deletions Source/.storybook/preview.css
Original file line number Diff line number Diff line change
@@ -1,19 +1,85 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
/* 1. Tailwind (includes preflight reset) — must come first */
@import "tailwindcss";
/* 2. PrimeReact theme — must come AFTER Tailwind so it overrides the preflight reset */
@import 'primereact/resources/themes/lara-dark-blue/theme.css';

html, body {
background-color: var(--surface-ground);
color: var(--text-color);
min-height: 100vh;
margin: 0;
font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
font-size: 12px;
line-height: 24px;
font-weight: 400;
}

.storybook-wrapper {
display: flex;
align-items: center;
justify-content: center;
min-height: 100vh;
padding: 1rem;
background-color: #f3f4f6; /* approx. Tailwind bg-gray-100 */
background-color: var(--surface-ground);
}

/* Utility classes matching Chronicle Workbench */
.highlight {
background-color: var(--highlight-bg);
color: var(--highlight-text-color);
}

.contentBackground {
background-color: var(--surface-0);
}

/* small helper to ensure Dialog widths appear nicely in Storybook */
.p-dialog .p-dialog-content {
.panel {
background: var(--surface-card);
border: 1px solid var(--surface-border);
border-radius: var(--border-radius);
color: var(--text-color);
display: flex;
align-items: center;
gap: 0.75rem;
flex-direction: column;
min-height: 0;
}

.card {
display: flex;
flex-direction: column;
min-height: 0;
}

/* DataTable layout helpers */
.p-datatable {
display: flex;
flex-direction: column;
min-height: 0;
height: 100%;
}

.p-datatable .p-datatable-wrapper {
flex: 1 1 auto;
min-height: 0;
overflow: auto;
}

.p-datatable .p-datatable-table {
width: 100%;
}

/* Prevent menubar from collapsing to hamburger on small screens */
.p-menubar .p-menubar-button {
display: none !important;
}

.p-menubar .p-menubar-root-list {
display: flex !important;
flex-direction: row !important;
position: static !important;
width: auto !important;
padding: 0 !important;
}

.p-menubar {
border: 1px solid var(--surface-border) !important;
}
11 changes: 8 additions & 3 deletions Source/.storybook/preview.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
// Copyright (c) Cratis. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

import 'primereact/resources/themes/lara-dark-blue/theme.css';
import 'primereact/resources/primereact.min.css';
import 'primeicons/primeicons.css';
import './preview.css';

export const parameters = {
actions: { argTypesRegex: '^on[A-Z].*' },
controls: { expanded: true }
controls: { expanded: true },
backgrounds: {
default: 'dark',
values: [
{ name: 'dark', value: '#111827' },
{ name: 'surface-card', value: '#1f2937' },
],
},
};
32 changes: 28 additions & 4 deletions Source/DataPage/DataPage.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import React from 'react';
import { Meta, StoryObj } from '@storybook/react';
import { DataPage, MenuItem } from './DataPage';
import { Column } from 'primereact/column';
import { QueryFor } from '@cratis/arc/queries';
import { QueryFor, QueryResult } from '@cratis/arc/queries';

const meta: Meta<typeof DataPage> = {
title: 'DataPage/DataPage',
Expand All @@ -26,17 +26,41 @@ interface Person {
role: string;
}

// Mock query
const mockPersons: Person[] = [
{ id: 1, name: 'Alice Johnson', email: 'alice@example.com', role: 'Admin' },
{ id: 2, name: 'Bob Smith', email: 'bob@example.com', role: 'Editor' },
{ id: 3, name: 'Carol White', email: 'carol@example.com', role: 'Viewer' },
{ id: 4, name: 'David Brown', email: 'david@example.com', role: 'Editor' },
{ id: 5, name: 'Eve Davis', email: 'eve@example.com', role: 'Admin' },
{ id: 6, name: 'Frank Miller', email: 'frank@example.com', role: 'Viewer' },
{ id: 7, name: 'Grace Wilson', email: 'grace@example.com', role: 'Editor' },
{ id: 8, name: 'Henry Taylor', email: 'henry@example.com', role: 'Viewer' },
];

// Mock query — overrides perform() to return static data instead of making HTTP calls
class PersonsQuery extends QueryFor<Person, object> {
readonly route = '/api/persons';
readonly routeTemplate = '/api/persons';
readonly defaultValue: Person = { id: 0, name: '', email: '', role: '' };
readonly defaultValue: Person = [] as unknown as Person;
readonly parameterDescriptors = [];
get requiredRequestParameters() {
return [];
}
constructor() {
super(Object, false);
super(Object, true);
}
override perform(): Promise<QueryResult<Person>> {
return Promise.resolve({
data: mockPersons,
paging: { totalItems: mockPersons.length, totalPages: 1, page: 0, size: mockPersons.length },
isSuccess: true,
isAuthorized: true,
isValid: true,
hasExceptions: false,
validationResults: [],
exceptionMessages: [],
exceptionStackTrace: '',
} as unknown as QueryResult<Person>);
}
}

Expand Down
58 changes: 41 additions & 17 deletions Source/DataTables/DataTableForObservableQuery.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import React, { useState } from 'react';
import { Meta, StoryObj } from '@storybook/react';
import { DataTableForObservableQuery } from './DataTableForObservableQuery';
import { Column } from 'primereact/column';
import { ObservableQueryFor } from '@cratis/arc/queries';
import { ObservableQueryFor, QueryResult, ObservableQuerySubscription } from '@cratis/arc/queries';
import { DataTableSelectionSingleChangeEvent } from 'primereact/datatable';

const meta: Meta<typeof DataTableForObservableQuery> = {
Expand All @@ -25,18 +25,42 @@ interface Task {
assignee: string;
}

// Mock observable query
const mockTasks: Task[] = [
{ id: 1, title: 'Design system architecture', status: 'done', priority: 'high', assignee: 'Alice' },
{ id: 2, title: 'Implement authentication', status: 'in-progress', priority: 'high', assignee: 'Bob' },
{ id: 3, title: 'Write unit tests', status: 'in-progress', priority: 'medium', assignee: 'Alice' },
{ id: 4, title: 'Set up CI/CD pipeline', status: 'done', priority: 'medium', assignee: 'Charlie' },
{ id: 5, title: 'Update documentation', status: 'todo', priority: 'low', assignee: 'Bob' },
{ id: 6, title: 'Performance profiling', status: 'todo', priority: 'medium', assignee: 'Charlie' },
{ id: 7, title: 'Security audit', status: 'todo', priority: 'high', assignee: 'Alice' },
{ id: 8, title: 'Dependency updates', status: 'in-progress', priority: 'low', assignee: 'Bob' },
];

// Mock observable query — overrides subscribe() to deliver static data instead of opening a WebSocket
class TasksQuery extends ObservableQueryFor<Task, object> {
readonly route = '/api/tasks';
readonly routeTemplate = '/api/tasks';
readonly defaultValue: Task = { id: 0, title: '', status: 'todo', priority: 'low', assignee: '' };
readonly defaultValue: Task = [] as unknown as Task;
readonly parameterDescriptors = [];
get requiredRequestParameters() {
return [];
}
constructor() {
super(Object, false);
}
override subscribe(callback: (result: QueryResult<Task>) => void): ObservableQuerySubscription<Task> {
callback({
data: mockTasks,
paging: { totalItems: mockTasks.length, totalPages: 1, page: 0, size: mockTasks.length },
isSuccess: true,
isAuthorized: true,
isValid: true,
hasExceptions: false,
validationResults: [],
exceptionMessages: [],
exceptionStackTrace: '',
} as unknown as QueryResult<Task>);
return { unsubscribe: () => undefined } as unknown as ObservableQuerySubscription<Task>;
}
}

const getStatusColor = (status: string) => {
Expand Down Expand Up @@ -75,21 +99,21 @@ export const Default: Story = {
>
<Column field="id" header="ID" sortable style={{ width: '10%' }} />
<Column field="title" header="Task Title" sortable style={{ width: '35%' }} />
<Column
field="status"
header="Status"
sortable
<Column
field="status"
header="Status"
sortable
style={{ width: '20%' }}
body={(rowData: Task) => (
<span className={getStatusColor(rowData.status)}>
{rowData.status}
</span>
)}
/>
<Column
field="priority"
header="Priority"
sortable
<Column
field="priority"
header="Priority"
sortable
style={{ width: '15%' }}
body={(rowData: Task) => (
<span className={getPriorityColor(rowData.priority)}>
Expand Down Expand Up @@ -119,10 +143,10 @@ export const WithSelection: Story = {
<Column selectionMode="single" headerStyle={{ width: '3rem' }} />
<Column field="id" header="ID" sortable style={{ width: '10%' }} />
<Column field="title" header="Task Title" sortable style={{ width: '35%' }} />
<Column
field="status"
header="Status"
sortable
<Column
field="status"
header="Status"
sortable
style={{ width: '20%' }}
body={(rowData: Task) => (
<span className={getStatusColor(rowData.status)}>
Expand All @@ -132,7 +156,7 @@ export const WithSelection: Story = {
/>
<Column field="assignee" header="Assignee" sortable style={{ width: '20%' }} />
</DataTableForObservableQuery>

{selectedTask && (
<div className="mt-4 p-4 border rounded">
<h3 className="font-bold mb-2">Selected Task:</h3>
Expand Down
33 changes: 28 additions & 5 deletions Source/DataTables/DataTableForQuery.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import React, { useState } from 'react';
import { Meta, StoryObj } from '@storybook/react';
import { DataTableForQuery } from './DataTableForQuery';
import { Column } from 'primereact/column';
import { QueryFor } from '@cratis/arc/queries';
import { QueryFor, QueryResult } from '@cratis/arc/queries';
import { DataTableSelectionSingleChangeEvent } from 'primereact/datatable';

const meta: Meta<typeof DataTableForQuery> = {
Expand All @@ -25,17 +25,40 @@ interface Product {
inStock: boolean;
}

// Mock query
const mockProducts: Product[] = [
{ id: 1, name: 'Wireless Headphones', category: 'Electronics', price: 79.99, inStock: true },
{ id: 2, name: 'Mechanical Keyboard', category: 'Electronics', price: 129.99, inStock: true },
{ id: 3, name: 'USB-C Hub', category: 'Electronics', price: 49.99, inStock: false },
{ id: 4, name: 'Standing Desk Mat', category: 'Office', price: 34.99, inStock: true },
{ id: 5, name: 'Monitor Stand', category: 'Office', price: 59.99, inStock: true },
{ id: 6, name: 'Webcam HD', category: 'Electronics', price: 89.99, inStock: false },
{ id: 7, name: 'Laptop Sleeve', category: 'Accessories', price: 24.99, inStock: true },
{ id: 8, name: 'Cable Management Kit', category: 'Accessories', price: 14.99, inStock: true },
];

// Mock query — overrides perform() to return static data instead of making HTTP calls
class ProductsQuery extends QueryFor<Product, object> {
readonly route = '/api/products';
readonly routeTemplate = '/api/products';
readonly defaultValue: Product = { id: 0, name: '', category: '', price: 0, inStock: false };
readonly defaultValue: Product = [] as unknown as Product;
readonly parameterDescriptors = [];
get requiredRequestParameters() {
return [];
}
constructor() {
super(Object, false);
super(Object, true);
}
override perform(): Promise<QueryResult<Product>> {
return Promise.resolve({
data: mockProducts,
paging: { totalItems: mockProducts.length, totalPages: 1, page: 0, size: mockProducts.length },
isSuccess: true,
isAuthorized: true,
isValid: true,
hasExceptions: false,
validationResults: [],
exceptionMessages: [],
exceptionStackTrace: '',
} as unknown as QueryResult<Product>);
}
}

Expand Down
10 changes: 6 additions & 4 deletions Source/Dialogs/BusyIndicatorDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@ export const BusyIndicatorDialog = (props: BusyIndicatorDialogRequest) => {
onCancel={() => undefined}
buttons={null}
>
<ProgressSpinner />
<p className="m-0">
{props.message}
</p>
<div className="flex flex-col items-center justify-center gap-4 py-4">
<ProgressSpinner />
<p className="m-0 text-center">
{props.message}
</p>
</div>
</Dialog>
);
};
Loading
Loading