Skip to content

Add initial component set#56

Merged
sjoerdbeentjes merged 24 commits into
mainfrom
feat/initial-component-set
Jun 30, 2026
Merged

Add initial component set#56
sjoerdbeentjes merged 24 commits into
mainfrom
feat/initial-component-set

Conversation

@sjoerdbeentjes

@sjoerdbeentjes sjoerdbeentjes commented Jun 23, 2026

Copy link
Copy Markdown
Collaborator

What

Adds the React & Angular component-library half of the PoC component inventory to @surfnet/react and @surfnet/angular, plus the supporting contracts.

Primitives vendored
React: (shadcn CLI / Base UI, Phosphor icons) — each with its own directory, barrel, src/index.ts export, and a Storybook story:
avatar, breadcrumb, checkbox, dropdown-menu, field, input, label, select, separator, sidebar, table. (button already existed.)
Angular, (spartan-ng CLI / brain, Phosphor icons) —each with its own directory, src\lib file with component files and Storybook and a src/index.ts export, Same components as for React, but addittionally also these because the desired components depend on these: icon, sheet, skeleton, tooltip.

DataTable
React — a reusable component built on the table primitive + TanStack Table (@tanstack/react-table), matching the dedicated "Data Table" Figma spec: row selection, sortable columns, text filter, column-visibility menu, row actions, and Previous/Next pagination. All user-facing strings (Columns, No results., Previous, Next, the selection summary, filter placeholder) are props with sensible defaults.
Angular — no data table yet, this would take considerably more effort to custom implement.

Contracts — every new component gets a <name>Contract in @surfnet/contracts as the cross-framework source of truth. Components with a real axis are tied to the contract (cva satisfies Record<…> or typed inline-union props): avatar size, dropdown-menu item variant, field orientation, select trigger size, sidebar menu-button variant/size. The rest are description-only.

Skill — updated add-component (SKILL.md, react.md, angular.md) to require a contract for every component and to document the three contract shapes (variant/size, orientation, description-only), closing the gap that previously left primitives without contracts.

Why

These are the primitives needed to rebuild the two PoC screens (App catalog, Login).

@sjoerdbeentjes sjoerdbeentjes changed the title feat(react): core component set + data table Add initial component set + data table Jun 23, 2026
@sjoerdbeentjes sjoerdbeentjes changed the title Add initial component set + data table Add initial component set Jun 23, 2026
@sjoerdbeentjes sjoerdbeentjes marked this pull request as draft June 23, 2026 13:14
@sjoerdbeentjes sjoerdbeentjes force-pushed the feat/initial-component-set branch from b735500 to 51d80d8 Compare June 23, 2026 13:14
@sjoerdbeentjes sjoerdbeentjes linked an issue Jun 23, 2026 that may be closed by this pull request
Vendor eleven shadcn/Base UI primitives into @surfnet/react — avatar,
breadcrumb, checkbox, dropdown-menu, field, input, label, select,
separator, sidebar, table — each with a barrel, an index export, and a
Storybook story. Add a DataTable component built on the table primitive +
TanStack Table (row selection, sorting, filtering, column visibility,
pagination); all of its user-facing strings are props with defaults.

Add a contract per component in @surfnet/contracts (description-only where
there is no axis) and tie each component to it via satisfies / typed props.
Update the add-component skill to require a contract for every component and
document the variant/size, orientation, and description-only shapes.
…cons

Follow PR #44: replace all @tabler/icons-react usages in the new components
and their stories with @phosphor-icons/react (`*Icon` suffix). The icon
library config and peer/dev deps already came in via the rebase onto main.
The separator story declared an `orientation` control but every story
hardcoded its render, so the control did nothing. Add an args-driven Default
playground that consumes `orientation` (and adapts its layout) so the control
works; keep the Horizontal/Vertical showcases.

Audited the rest: Storybook here only surfaces explicitly declared argTypes
(no prop inference), and separator was the only component with a declared-but-
unconsumed control. Document the rule in the add-component skill so new stories
always wire their controls to an args-driven story.
@sjoerdbeentjes sjoerdbeentjes force-pushed the feat/initial-component-set branch from 2692ebb to 0bc3dfa Compare June 24, 2026 09:23

@sjoerdbeentjes sjoerdbeentjes left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@loofpc Angular deel ziet er netjes uit! Paar dingen die we nog kunnen fixen:

  1. Contracts worden niet afgedwongen
  2. Icons zijn Lucide ipv Phosphor

`,
})
export class HlmAvatar extends BrnAvatar {
public readonly size = input<'default' | 'sm' | 'lg'>('default');

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deze as is hardcoded in plaats van gekoppeld aan @surfnet/contracts, waardoor de cross-framework parity aan de Angular-kant niet echt wordt afgedwongen. De story gebruikt avatarContract, maar deze type niet. Dit kan in de toekomst bij wijziging in avatarContract.sizes de React-build laten falen en hier niet.

We kunnen het laten voor nu en het Button component als voorbeeld nemen voor de contracts, maar denk dat het wel mooi is om het voor alle components te doen (misschien mooi werk voor een AI agent)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Goed punt. Ik stel voor de wijzigingen aan de helm-components in een aparte PR te doen en dit voor nu de gescaffolde components te laten.

@@ -0,0 +1,36 @@
import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';
import { NgIcon, provideIcons } from '@ng-icons/core';
import { lucideEllipsis } from '@ng-icons/lucide';

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We zijn inmiddels geswitcht naar Phosphor voor icons, dus denk ik ook mooi om dat in de angular components door te voeren.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Goed punt, ik had dat al gezien en heb al phosphor icons gebruikt in de Storybook story's. Ik stel voor de wijzigingen aan de helm-components in een aparte PR te doen en dit voor nu de gescaffolde components te laten.

The new helm components only consumed the contracts in their stories, so a
contract change failed the React build but compiled fine on the Angular side,
defeating the cross-framework parity guarantee.

Wire each axis-bearing component to its contract the way hlm-button already
does: avatar, select-trigger, and dropdown-menu-item type their input from the
contract name; field and sidebar-menu-button add `satisfies Record<…>` on
their cva variant maps so a stray or missing key fails `pnpm lint`.
There is no Angular data table yet and nothing imports the package. Remove it
from package.json and from ng-package.json allowedNonPeerDependencies.
};

/** Category select prefix — a dropdown trigger on the leading side. */
export const WithLeadingDropdown: Story = {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deze story lijkt functioneel niet goed te werken of is onduidelijk. Je hebt een input met een dropdown er voor, maar als je iets selecteert uit de dropdown dan doet dat niks. Ik zou verwachten dat de gekozen optie dan getoond wordt in het input field of zo. Dit is nu het geval zowel in React als Angular.

The vendored components shipped spartan's default @ng-icons/lucide glyphs
while the design system standardized on Phosphor (React migrated in PR #44)
and only the stories used it. Swap every component-side icon to its Phosphor
equivalent (matching React's choices, e.g. CaretUpDown on the select trigger),
promote @ng-icons/phosphor-icons to a runtime dependency, and remove
@ng-icons/lucide from package.json and ng-package.json.
The WithLeadingDropdown story (React and Angular) had a static "Category"
trigger and dropdown items that did nothing when selected. Track the chosen
category in state and reflect it in the trigger label and input placeholder so
the example demonstrates real behavior.
Add react-storybook (6006) and angular-storybook (6007) entries so the
component libraries can be launched for preview/verification.
…tton

The leading dropdown trigger used hlmBtn, which isn't among the story's
imported directives, so it matched nothing and rendered unstyled. Switch to
hlmInputGroupButton (already imported via HlmInputGroupImports), matching
React's InputGroupButton, so the ghost-variant button styling applies.
@sjoerdbeentjes sjoerdbeentjes marked this pull request as ready for review June 30, 2026 06:36
@sjoerdbeentjes sjoerdbeentjes merged commit 1922544 into main Jun 30, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Toevoegen componenten voor demo-pagina

2 participants