Content
View differences
Updated by Alexander Coles 24 days ago
### Goal
Extract the generic BorderBox list shell from `OpenProject::Common::WorkPackageCardListComponent` into a reusable BorderBox-backed list primitive into `OpPrimer::BorderBoxListComponent`, move then make `WorkPackageCardListComponent` render through the new generic header/action/menu chrome into that primitive, and keep OpenProject domain-specific item support outside `OpPrimer`. component.
This implementation is a static component extraction. Generic header actions, header menus, and item menus are in scope as reusable list chrome. Sorting, reordering, drag-and-drop ordering APIs, ordering, row action menus, header action menus, and action-menu abstractions beyond these explicit slots meetings adoption are out of scope for this work package.
### Component API
`OpPrimer::BorderBoxListComponent` should provide these generic slots:
- `header`
- `with_item`
- `with_empty_item`
- `with_work_package_item`
- `footer`
The public collection slot should be called `items`, to distinguish the wrapper API from the underlying `Primer::Beta::BorderBox` row implementation. Callers are responsible for iterating collections and calling item slot methods in the order they want items rows rendered. The component should render through Primer Primer's BorderBox under the hood and forward relevant system arguments such as ids, list ids, padding, classes, data attributes, and ARIA attributes.
The generic header should support title, optional count, description, action buttons, and a menu slot. The generic item should render `with_item` renders arbitrary content and expose an optional menu slot. The footer should continue to render as a BorderBox row rather than a BorderBox footer, preserving the existing list-body DOM contract.
`OpPrimer::BorderBoxListComponent` must not expose work-package-specific slots or classes.
### OpenProject list specialization
Add `OpenProject::Common::BorderBoxListComponent` as the manual OpenProject-specialized layer around `OpPrimer::BorderBoxListComponent`.
It should compose content. `with_empty_item` renders an `OpPrimer::BorderBoxListComponent` instance, delegate the generic slot methods to it, and provide the generic list slots plus:
- explicit empty row. `with_work_package_item`
`with_work_package_item` provides a work-package item row bridge through a configurable item/card component, without hard-coding backlog-specific behavior into the generic component. This keeps work-package support in `OpenProject::Common`, while still allowing manual callers to interleave work-package items with custom items. This component must not accept `work_packages:` and must not perform automatic collection iteration.
The work-package item component class should be configurable once on the list through a constructor-level `work_package_item_component:` default, with per-item `component_klass:` override support.
This component is also the future home for other explicit OpenProject-specialized item slots, such as meeting items, if those become useful.
### Work package wrapper
`OpenProject::Common::WorkPackageCardListComponent` should remain the automatic collection-oriented wrapper. become a convenience wrapper around `OpPrimer::BorderBoxListComponent`.
It should continue to support:
- `project:`
- `container:`
- `work_packages:`
- `drag_and_drop:`
- `item_component_klass:`
- `params:`
- `current_user:`
- delegated `header`
- delegated `footer`
- `empty_state`
When `work_packages:` are passed, `WorkPackageCardListComponent` should iterate the collection itself and add work-package items through `OpenProject::Common::BorderBoxListComponent`. rows to the underlying `BorderBoxListComponent`. When no work packages are rendered, it should render `empty_state` through the underlying empty item slot.
`WorkPackageCardListComponent` should not expose manual item composition. Manual interleaving belongs to `OpenProject::Common::BorderBoxListComponent`.
### Backlogs adoption
`Backlogs::InboxComponent` should use `OpenProject::Common::BorderBoxListComponent` directly, because it interleaves a custom show-more item between work-package items.
`Backlogs::SprintComponent` and `Backlogs::BucketComponent` should stay on `OpenProject::Common::WorkPackageCardListComponent`, because they render automatic work-package collections.
### Compatibility expectations
Existing backlogs rendering should keep the relevant DOM contract:
- container id from `dom_target(container)`
- list id from `dom_target(container, :list)`
- header id and collapsible linkage
- work package item row ids, classes, data attributes, and card rendering
- empty item row marker
- footer item row styling
- drag-and-drop data on the box
### Tests
Add or update focused component specs for `OpPrimer::BorderBoxListComponent` covering:
- header, generic item, empty item, item menu, work-package item, and footer rendering
- forwarding of system arguments to the underlying BorderBox
- header description, actions, menu, and count behavior
- empty item data merging
- no work-package references under `OpPrimer`
Add focused component specs for `OpenProject::Common::BorderBoxListComponent` covering:
- generic item pass-through through delegated slots
- constructor-level `work_package_item_component:` default
- per-item `component_klass:` override
- work-package item rendering through a supplied component class
- work-package item customization blocks
- no automatic collection iteration bridge behavior
Update/keep `WorkPackageCardListComponent` specs covering:
- automatic `work_packages:` iteration
- delegated header and footer rendering
- `empty_state` rendering through the empty item slot
- container/list/header ids
- existing backlogs item row data/classes and drag-and-drop data
Update backlogs component specs to ensure the inbox show-more item remains interleaved and sprint/bucket DOM contracts remain stable.
Extract
This implementation is a static component extraction. Generic header actions, header menus, and item menus are in scope as reusable list chrome. Sorting, reordering, drag-and-drop ordering APIs,
### Component API
`OpPrimer::BorderBoxListComponent` should provide these generic slots:
- `header`
- `with_item`
- `with_empty_item`
-
-
The public collection slot should be called `items`, to distinguish the wrapper API from the underlying `Primer::Beta::BorderBox` row implementation. Callers are responsible for
The generic header should support title, optional count, description, action buttons, and a menu slot. The generic item should render
`OpPrimer::BorderBoxListComponent` must not expose work-package-specific slots or classes.
### OpenProject list specialization
Add `OpenProject::Common::BorderBoxListComponent` as the manual OpenProject-specialized layer around `OpPrimer::BorderBoxListComponent`.
It should compose
-
`with_work_package_item` provides a work-package item
The work-package item component class should be configurable once on the list through a constructor-level `work_package_item_component:` default, with per-item `component_klass:` override support.
This component is also the future home for other explicit OpenProject-specialized item slots, such as meeting items, if those become useful.
### Work package wrapper
`OpenProject::Common::WorkPackageCardListComponent` should remain the automatic collection-oriented wrapper.
It should continue to support:
- `project:`
- `container:`
- `work_packages:`
- `drag_and_drop:`
- `item_component_klass:`
- `params:`
- `current_user:`
- delegated `header`
- delegated `footer`
- `empty_state`
When `work_packages:` are passed, `WorkPackageCardListComponent` should iterate the collection itself and add work-package items through `OpenProject::Common::BorderBoxListComponent`.
`WorkPackageCardListComponent` should not expose manual item composition. Manual interleaving belongs to `OpenProject::Common::BorderBoxListComponent`.
### Backlogs adoption
`Backlogs::InboxComponent` should use `OpenProject::Common::BorderBoxListComponent` directly, because it interleaves a custom show-more item between work-package items.
`Backlogs::SprintComponent` and `Backlogs::BucketComponent` should stay on `OpenProject::Common::WorkPackageCardListComponent`, because they render automatic work-package collections.
### Compatibility expectations
Existing backlogs rendering should keep the relevant DOM contract:
- container id from `dom_target(container)`
- list id from `dom_target(container, :list)`
- header id and collapsible linkage
- work package item
- empty item
- footer item
- drag-and-drop data on the box
### Tests
Add or update focused component specs for `OpPrimer::BorderBoxListComponent` covering:
- header, generic item, empty item, item menu,
- forwarding of system arguments to the underlying BorderBox
- header description, actions, menu, and count behavior
- empty item data merging
- no work-package references under `OpPrimer`
Add focused component specs for `OpenProject::Common::BorderBoxListComponent` covering:
- generic item pass-through through delegated slots
- constructor-level `work_package_item_component:` default
- per-item `component_klass:` override
- work-package item rendering through a supplied component class
- work-package item customization blocks
- no automatic collection iteration
Update/keep `WorkPackageCardListComponent` specs covering:
- automatic `work_packages:` iteration
- delegated header and footer rendering
- `empty_state` rendering through the empty item slot
- container/list/header ids
- existing backlogs item
Update backlogs component specs to ensure the inbox show-more item remains interleaved and sprint/bucket DOM contracts remain stable.