Skip to content

spadrapo/drapo

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

319 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Drapo

Build Status License NuGet

Drapo is a declarative Single Page Application (SPA) framework that enables you to build dynamic web applications using HTML attributes instead of writing JavaScript. With Drapo, you can implement data binding, control flow, event handling, and complex UI logic directly in your HTML markup using intuitive d-* attributes.

πŸš€ Live Demo

✨ Key Features

  • 🏷️ Declarative HTML: Build interactive UIs using HTML attributes - no JavaScript required
  • πŸ”„ Two-way Data Binding: Automatic synchronization between UI and data using mustache syntax {{}}
  • 🎯 Control Flow: Built-in d-for loops and d-if conditionals
  • 🎨 Dynamic Styling: Conditional CSS classes with d-class
  • πŸ“‘ Real-time Updates: WebSocket support via SignalR for live data
  • 🧩 Component System: Reusable UI components with lifecycle management
  • πŸ›£οΈ SPA Routing: Client-side routing for single page applications
  • βœ… Validation: Built-in form validation with custom rules
  • 🎨 Theming: Dynamic theme switching support
  • πŸ—οΈ .NET Integration: ASP.NET Core middleware for seamless server integration

πŸš€ Quick Start

Installation

Install the NuGet package in your ASP.NET Core project:

dotnet add package Drapo

Or via Package Manager Console:

Install-Package Drapo

Setup

Add Drapo to your ASP.NET Core application in Startup.cs or Program.cs:

// Program.cs (.NET 6+)
builder.Services.AddDrapo();
app.UseDrapo();

// Or Startup.cs (.NET Core 3.1)
public void ConfigureServices(IServiceCollection services)
{
    services.AddDrapo();
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    app.UseDrapo();
}

Your First Drapo Page

Create an HTML file with Drapo attributes:

<!DOCTYPE html>
<html>
<head>
    <script src="/drapo.js"></script>
    <title>My First Drapo App</title>
</head>
<body>
    <!-- Define data -->
    <div d-dataKey="user" d-dataType="object" 
         d-dataProperty-name="John" 
         d-dataProperty-age="25"></div>
    
    <!-- Display and edit data -->
    <h1>Hello, {{user.name}}!</h1>
    <p>Age: <input d-model="{{user.age}}" type="number" /></p>
    
    <!-- Dynamic list -->
    <div d-dataKey="items" d-dataType="array" 
         d-dataProperty-0="Apple" 
         d-dataProperty-1="Banana" 
         d-dataProperty-2="Orange"></div>
    
    <ul>
        <li d-for="item in items">{{item}}</li>
    </ul>
    
    <!-- Event handling -->
    <button d-on-click="AddDataItem(items, 'New Item')">Add Item</button>
</body>
</html>

πŸ—οΈ Architecture

Drapo consists of two main components:

  1. Client-side Framework (TypeScript/JavaScript): Handles DOM manipulation, data binding, and UI logic
  2. Server-side Middleware (ASP.NET Core): Provides data services, routing, and server integration
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   Browser       β”‚    β”‚   ASP.NET Core   β”‚
β”‚                 β”‚    β”‚                  β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚    β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚  β”‚ HTML      β”‚  │◄────  β”‚ Drapo       β”‚ β”‚
β”‚  β”‚ + d-*     β”‚  β”‚    β”‚  β”‚ Middleware  β”‚ β”‚
β”‚  β”‚ attributesβ”‚  β”‚    β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚    β”‚                  β”‚
β”‚                 β”‚    β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚    β”‚  β”‚ Your        β”‚ β”‚
β”‚  β”‚ drapo.js  β”‚  │◄────  β”‚ Controllers β”‚ β”‚
β”‚  β”‚ (Client)  β”‚  β”‚    β”‚  β”‚ & Services  β”‚ β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚    β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

πŸ“– Getting Started

Prerequisites

npm install -g typescript

Development Setup

  1. Clone the repository:
git clone https://github.com/spadrapo/drapo.git
cd drapo
  1. Build the project:
cd src
dotnet build
  1. Run the demo application:
cd Web/WebDrapo
dotnet run

Visit https://localhost:5001 to see the demo application.

πŸ“‹ API Reference

Data Binding

Attribute Description Example
d-dataKey Defines a data context with a unique key d-dataKey="user"
d-dataType Specifies the data type (object, array, string, etc.) d-dataType="object"
d-dataProperty-{name} Sets initial property values d-dataProperty-name="John"
d-dataUrl URL for loading data d-dataUrl="~/api/users"
d-dataUrlGet URL for GET operations d-dataUrlGet="~/api/users/get"
d-dataUrlSet URL for POST/PUT operations d-dataUrlSet="~/api/users/save"
d-model Two-way data binding d-model="{{user.name}}"

Control Flow

Attribute Description Example
d-for Iterates over arrays or objects d-for="item in items"
d-if Conditional rendering d-if="{{user.isActive}}"
d-class Dynamic CSS classes d-class="{active:{{item.selected}}}"

Events

Attribute Description Example
d-on-click Click event handler d-on-click="SaveData(user)"
d-on-change Change event handler d-on-change="UpdateTotal()"
d-on-keyup Keyup event handler d-on-keyup="FilterItems()"
d-on-keyup-enter Enter key specific handler d-on-keyup-enter="SubmitForm()"
d-on-blur Focus lost event d-on-blur="ValidateField()"

Components & Layout

Attribute Description Example
d-parent Defines the default parent for partial pages d-parent="main"
d-parentSector Default sector of parent for child content d-parentSector="content"
d-child Defines default child for a sector d-child="sidebar"
d-childSector Defines a default child sector d-childSector="widgets"

Advanced Features

Attribute Description Example
d-dragStart Enable drag functionality d-dragStart="true"
d-dragEnd Enable drop functionality d-dragEnd="true"
d-resize Enable element resizing d-resize-location="width"
d-cloak Hide element until data is loaded d-cloak
d-viewport Enable virtual scrolling for large lists d-viewport="true"

Built-in Functions

Drapo provides many built-in functions for common operations:

Function Description Example
AddDataItem(dataKey, item) Add item to array AddDataItem(users, newUser)
RemoveDataItem(dataKey, item) Remove item from array RemoveDataItem(users, user)
PostData(dataKey) Save data to server PostData(users)
ClearData(dataKey) Clear/reset data ClearData(form)
CheckDataField(dataKey, field) Set field to true CheckDataField(config, enabled)
UncheckDataField(dataKey, field) Set field to false UncheckDataField(config, enabled)
UpdateData(dataKey, data) Update entire data object UpdateData(user, newUserData)
CreateGuid() Generate unique identifier CreateGuid()

πŸ’‘ Examples

Todo List Application

<!DOCTYPE html>
<html>
<head>
    <script src="/drapo.js"></script>
    <title>Todo App</title>
</head>
<body>
    <!-- Data definitions -->
    <div d-dataKey="todos" d-dataType="array"></div>
    <div d-dataKey="newTodo" d-dataType="object" d-dataProperty-text=""></div>
    
    <!-- Add new todo -->
    <div>
        <input d-model="{{newTodo.text}}" 
               placeholder="What needs to be done?"
               d-on-keyup-enter="AddDataItem(todos,newTodo);ClearDataField(newTodo,text)">
        <button d-on-click="AddDataItem(todos,newTodo);ClearDataField(newTodo,text)">
            Add Todo
        </button>
    </div>
    
    <!-- Todo list -->
    <ul>
        <li d-for="todo in todos" d-class="{completed:{{todo.completed}}}">
            <input type="checkbox" d-model="{{todo.completed}}">
            <span>{{todo.text}}</span>
            <button d-on-click="RemoveDataItem(todos,todo)">Delete</button>
        </li>
    </ul>
    
    <!-- Summary -->
    <p d-if="{{todos.length > 0}}">
        Total: {{todos.length}} items
    </p>
</body>
</html>

Data-driven Form

<!-- User form with validation -->
<div d-dataKey="user" d-dataType="object" 
     d-dataUrlSet="~/api/users/save">
    
    <form>
        <div>
            <label>Name:</label>
            <input d-model="{{user.name}}" required>
        </div>
        
        <div>
            <label>Email:</label>
            <input d-model="{{user.email}}" type="email" required>
        </div>
        
        <div>
            <label>Country:</label>
            <select d-model="{{user.country}}">
                <option d-for="country in countries" value="{{country.code}}">
                    {{country.name}}
                </option>
            </select>
        </div>
        
        <button d-on-click="PostData(user)" type="button">
            Save User
        </button>
    </form>
</div>

Dynamic Content Loading

<!-- Load data from server -->
<div d-dataKey="products" 
     d-dataUrlGet="~/api/products"
     d-dataUrl="~/api/products">
    
    <div d-if="{{products.length == 0}}">
        Loading products...
    </div>
    
    <div d-for="product in products" class="product-card">
        <h3>{{product.name}}</h3>
        <p>Price: ${{product.price}}</p>
        <button d-on-click="AddToCart(product)">Add to Cart</button>
    </div>
</div>

πŸ”’ Security

WebSocket Origin Validation

Drapo automatically validates WebSocket connection origins to prevent Cross-Site WebSocket Hijacking (CSWSH) attacks. By default, only connections from the same origin as your application are allowed.

Configuration

You can configure WebSocket origin validation in your Startup.cs:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    app.UseDrapo(options => 
    {
        // Enable/disable origin validation (enabled by default)
        options.Config.ValidateWebSocketOrigin = true;
        
        // Optional: Specify allowed origins explicitly
        // Note: Scheme (http/https) is ignored during validation
        options.Config.AllowedWebSocketOrigins = new List<string>
        {
            "https://yourdomain.com",
            "https://www.yourdomain.com",
            "https://subdomain.yourdomain.com"
        };
    });
}

Gateway Support: The origin validation automatically supports scenarios where an external gateway or load balancer handles HTTPS, while the internal application runs on HTTP. Only the host/domain portion is compared, not the scheme.

Default Behavior

  • Origin validation is enabled by default for security
  • The current request's host is always allowed (scheme-independent to support gateways)
  • If an explicit allow-list is configured, origins in that list are also allowed (in addition to the current host)
  • Both Origin and Referer headers are checked
  • Invalid or missing headers result in connection rejection
  • Scheme is ignored during validation - only the host/domain is compared, allowing gateways to handle HTTPS while the internal application uses HTTP

Disabling Validation

⚠️ Not recommended for production: You can disable origin validation if needed:

options.Config.ValidateWebSocketOrigin = false;

πŸ§ͺ Testing

Drapo includes a comprehensive testing framework using Selenium WebDriver and NUnit to validate the framework's functionality across different browsers and scenarios.

Test Structure

The testing system consists of:

  • Pages Folder (src/Test/WebDrapo.Test/Pages/): Contains HTML test pages with expected evaluated HTML output
  • ReleaseTest Class (src/Test/WebDrapo.Test/ReleaseTest.cs): Initializes the WebDriver, defines test cases, and validation methods
  • Test Settings File (Test.runsettings): Runtime configuration including URLs for the WebDrapo instance

Running Tests

  1. Prerequisites: Ensure you have Chrome WebDriver installed and WebDrapo running locally

  2. Configuration: Update the URL in Test.Debug.runsettings to match your local WebDrapo instance:

    <Parameter name="webAppUrl" value="https://localhost:5001/" />
  3. Run Tests: Execute tests via Visual Studio Test Explorer or command line:

    dotnet test src/Test/WebDrapo.Test/

Adding New Tests

For each new page added to DrapoPages, follow these steps to create corresponding tests:

  1. Start WebDrapo: Run the application in debug mode and verify the URL matches your test settings

  2. Configure Test Settings: In Visual Studio, go to Test Menu β†’ Test Settings and select the appropriate .runsettings file

  3. Create Test Page: Add a new HTML file in the Pages folder with naming format: {PageName}.Test.html

    • Set the file's Build Action to "Embedded Resource"
  4. Create Test Method: In ReleaseTest.cs, add a new test method:

    [TestCase]
    public void YourPageNameTest()
    {
        ValidatePage("YourPageName");
    }
  5. Generate Expected Output:

    • Run the test in debug mode
    • After WebDriver navigation, copy the PageSource content
    • Paste it into your test HTML file (without formatting)
    • Save and re-run the test to verify it passes

Test Categories

The test suite covers various Drapo features:

  • Data Binding: Two-way binding, model updates, data synchronization
  • Control Flow: d-for loops, d-if conditionals, nested structures
  • Events: Click handlers, keyboard events, form interactions
  • Components: Component lifecycle, data passing, sector management
  • Validation: Form validation, custom rules, error handling
  • Advanced Features: Drag & drop, resizing, viewport scrolling

🀝 Contributing

We welcome contributions to Drapo! Here's how to get started:

Development Setup

  1. Fork and Clone:

    git clone https://github.com/your-username/drapo.git
    cd drapo
  2. Install Dependencies:

    cd src/Middleware/Drapo
    npm install
  3. Build the Project:

    cd src
    dotnet build
  4. Run Tests:

    dotnet test

Development Workflow

  1. Create a Feature Branch: git checkout -b feature/your-feature-name
  2. Make Changes: Implement your feature or bug fix
  3. Add Tests: Ensure your changes are covered by tests
  4. Build and Test: Verify everything works correctly
  5. Submit Pull Request: Create a PR with a clear description

Code Style

  • TypeScript: Follow the existing TSLint configuration
  • C#: Use standard .NET conventions
  • HTML: Use consistent indentation and attribute ordering
  • Commit Messages: Use clear, descriptive commit messages

Reporting Issues

When reporting bugs or requesting features:

  1. Check existing issues first
  2. Provide a minimal reproduction case
  3. Include browser and .NET version information
  4. Describe expected vs actual behavior

πŸ“š Documentation

πŸ“¦ Packages & Versions

  • NuGet Package: Drapo
  • Supported Frameworks:
    • .NET 8.0
    • .NET 6.0
    • .NET Core 3.1
  • Browser Support: Modern browsers (Chrome, Firefox, Safari, Edge)

πŸ“„ License

This project is licensed under the Apache License 2.0 - see the LICENSE file for details.

πŸ™ Acknowledgments

  • Built with ASP.NET Core and TypeScript
  • Uses SignalR for real-time communication
  • Inspired by modern declarative UI frameworks
  • Thanks to all contributors and users of the Drapo framework

Made with ❀️ by the Drapo team

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors