Files
2025-10-14 16:02:18 +00:00

313 lines
6.6 KiB
Markdown

# Quantity Control
An accessible, feature-rich Angular quantity selector component with dropdown presets and manual input mode.
## Features
-**Dropdown with presets** - Quick selection from predefined values
-**Edit mode** - Manual input for values beyond presets
-**Smart logic** - Automatically shows/hides Edit based on constraints
-**Flexible range** - Start from any value (0, 1, or custom)
-**Full accessibility** - WCAG 2.1 AA compliant with screen reader support
-**Keyboard navigation** - Arrow keys, Home, End, Enter, Escape
-**Form integration** - Implements `ControlValueAccessor`
-**Type-safe** - Full TypeScript support with proper validation
## Installation
```typescript
import { QuantityControlComponent } from '@isa/shared/quantity-control';
@Component({
// ...
imports: [QuantityControlComponent],
})
```
## Basic Usage
### Standalone
```html
<shared-quantity-control [value]="quantity" />
```
### With Reactive Forms
```typescript
export class MyComponent {
quantityControl = new FormControl(1);
}
```
```html
<shared-quantity-control [formControl]="quantityControl" />
```
### With Template-Driven Forms
```html
<shared-quantity-control [(ngModel)]="quantity" />
```
## API
### Inputs
| Input | Type | Default | Description |
|-------|------|---------|-------------|
| `value` | `model<number>` | `1` | Current quantity value (two-way binding) |
| `disabled` | `model<boolean>` | `false` | Whether the control is disabled |
| `min` | `number` | `1` | Minimum selectable value (starting point) |
| `max` | `number \| undefined` | `undefined` | Maximum selectable value (e.g., stock available) |
| `presetLimit` | `number` | `10` | Number of preset options before requiring Edit |
| `ariaLabel` | `string` | `undefined` | Custom ARIA label for accessibility |
### How It Works
**Preset options generated:** `min` to `(min + presetLimit - 1)`
**Edit option shown when:**
- `max` is `undefined` (unlimited), OR
- `max > (min + presetLimit - 1)` (stock exceeds presets)
## Examples
### Standard Use Case (1-10 with unlimited)
```html
<shared-quantity-control
[(value)]="quantity"
[min]="1"
[presetLimit]="10"
/>
```
**Dropdown:** 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, Edit ✅
---
### Start From Zero
```html
<shared-quantity-control
[(value)]="quantity"
[min]="0"
[presetLimit]="10"
/>
```
**Dropdown:** 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, Edit ✅
---
### Limited Stock (No Edit)
```html
<shared-quantity-control
[(value)]="quantity"
[min]="1"
[max]="5"
[presetLimit]="10"
/>
```
**Dropdown:** 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 (No Edit - max is 5)
---
### High Stock (With Edit)
```html
<shared-quantity-control
[(value)]="quantity"
[min]="1"
[max]="50"
[presetLimit]="20"
/>
```
**Dropdown:** 1, 2, 3, ... 20, Edit ✅ (allows 21-50)
---
### Custom Range (5-15)
```html
<shared-quantity-control
[(value)]="quantity"
[min]="5"
[presetLimit]="11"
[max]="15"
/>
```
**Dropdown:** 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 (No Edit)
---
### With Reactive Forms
```typescript
export class ShoppingCartComponent {
quantityControl = new FormControl(1, [
Validators.min(1),
Validators.max(99)
]);
}
```
```html
<shared-quantity-control
[formControl]="quantityControl"
[min]="1"
[max]="99"
[presetLimit]="10"
/>
```
---
### Disabled State
```html
<shared-quantity-control
[(value)]="quantity"
[disabled]="true"
/>
```
---
### Custom ARIA Label
```html
<shared-quantity-control
[(value)]="quantity"
[ariaLabel]="'Select number of items to purchase'"
/>
```
## Accessibility
The component implements the **ARIA combobox pattern** and is fully accessible:
-**Keyboard Navigation:**
- `Space/Enter` - Open dropdown
- `Arrow Up/Down` - Navigate options
- `Home/End` - Jump to first/last option
- `Enter/Space` - Select option
- `Escape` - Close dropdown or cancel edit
-**ARIA Attributes:**
- `role="combobox"` on host
- `role="listbox"` on dropdown
- `role="option"` on each item
- `aria-expanded`, `aria-haspopup`, `aria-controls`
- `aria-activedescendant` for keyboard focus
- `aria-selected` for current value
-**Screen Reader Support:**
- Value changes announced
- Edit mode transitions announced
- Validation errors announced
-**E2E Testing:**
- `data-what="quantity-control"` on button
- `data-what="quantity-control-option"` on options
- `data-which` attributes with values
## Validation
### Automatic Validation
The component validates input in Edit mode:
```typescript
// If min=1, entering 0 shows:
"Invalid quantity. Please enter a number greater than or equal to 1."
// If max=50, entering 100 shows:
"Invalid quantity. Maximum available is 50."
```
### Form Validators
Use standard Angular validators with the form control:
```typescript
quantityControl = new FormControl(1, [
Validators.required,
Validators.min(0),
Validators.max(999),
]);
```
## Behavior
### Dropdown
- Click button or press `Space`/`Enter` to open
- Click option to select
- Click outside (backdrop) to close
- Press `Escape` to close
### Edit Mode
- Select "Edit" option from dropdown
- Input field opens with current value pre-selected
- Press `Enter` to save
- Press `Escape` to cancel (reverts to original value)
- Click outside (blur) to save
## Styling
The component uses scoped CSS with these CSS classes:
```css
.quantity-control-button /* Main button */
.quantity-control-value /* Value display */
.quantity-control-input /* Edit mode input */
.options-list /* Dropdown container */
.quantity-control-option /* Each option */
.quantity-control-option.active /* Keyboard focused */
.quantity-control-option.selected /* Current value */
```
### Host Classes
```css
:host.disabled /* Disabled state */
:host.open /* Dropdown open */
:host.edit-mode /* Edit mode active */
```
## Performance
-**OnPush change detection** for optimal performance
-**Signal-based reactivity** with computed values
-**Efficient keyboard manager** from Angular CDK
-**Automatic cleanup** on component destruction
## Browser Support
- Chrome/Edge (latest)
- Firefox (latest)
- Safari (latest)
- Mobile browsers (iOS Safari, Chrome Android)
## Dependencies
- `@angular/core` ^20.1.2
- `@angular/forms` ^20.1.2
- `@angular/cdk` ^20.1.2
- `@ng-icons/core` (for chevron icons)
## Contributing
This component is part of the ISA Frontend monorepo. For changes:
1. Update component code
2. Update tests (when available)
3. Update Storybook stories
4. Update this README if API changes
## License
Internal use only - ISA Frontend Project