Skip to content

refactor(kol-link): component to use skeleton pattern - #10110

Open
deleonio wants to merge 114 commits into
developfrom
claude/migrate-kollink-skeleton-W6i6v
Open

refactor(kol-link): component to use skeleton pattern#10110
deleonio wants to merge 114 commits into
developfrom
claude/migrate-kollink-skeleton-W6i6v

Conversation

@deleonio

@deleonio deleonio commented May 6, 2026

Copy link
Copy Markdown
Contributor

Summary

Refactored the kol-link component to follow the controller pattern used by other KoliBri components, moving business logic from the component class into a dedicated LinkController and extracting the render logic into a functional component (LinkFC).

Key Changes

  • Component Architecture: Migrated KolLinkWc from a monolithic component to extend BaseWebComponent<LinkApi>, delegating logic to LinkController

    • Removed ~188 lines of validation and state management code from the component
    • Introduced LinkController to handle all prop validation, state management, and lifecycle hooks
    • Created LinkFC functional component for rendering the anchor element and tooltip
  • Props System: Extracted link-specific prop definitions into dedicated modules

    • Added new prop definition files: access-key.ts, aria-controls.ts, aria-current-value.ts, aria-description.ts, aria-expanded.ts, aria-owns.ts, custom-class.ts, disabled.ts, download.ts, inline.ts, link-callbacks.ts, link-label.ts, link-role.ts, short-key.ts, tab-index.ts, target.ts, tooltip-align.ts, variant.ts
    • Created linkPropsConfig to define the component's API surface
  • API Definition: Introduced LinkApi type that defines the component's public interface including methods (focus, click), refs, and state

  • Tooltip Management: Moved tooltip controller integration into LinkController, simplifying the component's render method

  • Event Handling: Simplified click handler (handleAnchorClick) by delegating validation and state checks to the controller

  • Lifecycle: Consolidated all prop validation into componentWillLoad via the controller, removing individual watch methods from the component

Implementation Details

  • The component now uses getRenderProp() to access validated, normalized prop values from the controller
  • Anchor ref management is handled by the controller via setAnchorRef() and getAnchorRef()
  • The ariaCurrent state is managed by the component but updated by the controller based on location changes
  • Tooltip visibility is controlled through the controller's hideTooltip() method
  • All prop watchers delegate to corresponding controller methods with consistent naming (watch* instead of validate*)

https://claude.ai/code/session_01DBHvCfXeuCiPnhiim3k4PZ

@github-actions

github-actions Bot commented May 6, 2026

Copy link
Copy Markdown
Contributor

🚀 MCP preview deployed to Vercel: https://kolibri-4n5ri6yop-public-ui-kolibri-mcp.vercel.app

@github-actions

github-actions Bot commented May 6, 2026

Copy link
Copy Markdown
Contributor

🚀 Preview deployed to GitHub Pages: https://public-ui.github.io/kolibri/pr-10110/265a1c5/
Commit: 265a1c5

@github-actions

github-actions Bot commented May 6, 2026

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request refactors the KolLinkWc component to utilize a new controller-based architecture and internal functional components, centralizing property definitions and validation logic. The review feedback highlights several critical issues in the new property normalizers—specifically for ariaCurrentValue, ariaExpanded, linkCallbacks, linkLabel, and tooltipAlign—where null or undefined inputs currently trigger errors instead of returning default values. Additionally, a regression in type safety was identified, as specific schema types for public props were replaced with generic strings, reducing IDE support and documentation for end-users.

Comment thread packages/components/src/internal/props/aria-current-value.ts
Comment thread packages/components/src/internal/props/aria-expanded.ts
Comment thread packages/components/src/internal/props/link-callbacks.ts
Comment thread packages/components/src/internal/props/link-label.ts
Comment thread packages/components/src/internal/props/tooltip-align.ts
Comment thread packages/components/src/components/link/component.tsx Outdated
@deleonio deleonio changed the title Refactor kol-link component to use controller pattern refact(kol-link): component to use controller pattern May 6, 2026
@deleonio deleonio changed the title refact(kol-link): component to use controller pattern refact(kol-link): component to use skeleton pattern May 6, 2026
@deleonio
deleonio marked this pull request as draft May 6, 2026 10:42
@deleonio deleonio changed the title refact(kol-link): component to use skeleton pattern refactor(kol-link): component to use skeleton pattern May 6, 2026
@deleonio
deleonio marked this pull request as ready for review May 6, 2026 13:33
@deleonio
deleonio marked this pull request as draft May 6, 2026 13:33
@deleonio

deleonio commented May 7, 2026

Copy link
Copy Markdown
Contributor Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request refactors the kol-link component by merging the kol-link-wc implementation into a unified public component and migrating logic to a functional component and controller structure. All dependent components and snapshots have been updated to reflect this change, and a new suite of internal prop definitions has been introduced. Review feedback identifies that the regular expressions for customClass and variant are too restrictive for short names, and an accessibility issue occurs when hideLabel is used without a string label. Further improvements were suggested regarding the redundant default tabIndex, the use of the deprecated onKeyPress event, and the limited range of allowed ARIA roles.

Comment thread packages/components/src/internal/props/custom-class.ts Outdated
Comment thread packages/components/src/internal/props/variant.ts Outdated
Comment thread packages/components/src/internal/props/tab-index.ts
Comment thread packages/components/src/internal/functional-components/link/component.tsx Outdated
Comment thread packages/components/src/internal/props/link-role.ts
@deleonio
deleonio force-pushed the claude/migrate-kollink-skeleton-W6i6v branch 4 times, most recently from 14781c4 to ff32431 Compare May 7, 2026 01:34
@deleonio

deleonio commented May 7, 2026

Copy link
Copy Markdown
Contributor Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request implements the 'FC-First Composition' architectural rule, which mandates the use of Functional Components instead of KoliBri web component tags within render methods to reduce browser overhead and lifecycle complexity. The refactoring introduces a new LinkFC and LinkController, which are integrated across several components including Breadcrumb, Form, Nav, and Toolbar. Feedback identifies a missing property mapping in the link controller initialization utility, incorrect heading numbering in the migration guide, and broken markdown syntax in the architectural documentation table.

Comment thread .claude/commands/migrate-to-skeleton.md Outdated
Comment thread packages/components/src/components/_skeleton/ARC42.md Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Refactors kol-link to the BaseWebComponent/Controller/FC skeleton pattern by extracting business logic into LinkController, the render into a LinkFC functional component, and per-prop configuration into many new prop-definition modules. All previous consumers of the internal <kol-link-wc> tag (nav, breadcrumb, skip-nav, toolbar, tree-item, table-stateless, form, link-button) are migrated to instantiate LinkController locally and render with LinkFC. The internal KolLinkWcTag element is removed.

Changes:

  • Introduce LinkController, LinkFC, and LinkApi / linkPropsConfig for the new skeleton-based link.
  • Add a large set of per-prop definition modules under internal/props/ and export them from the props barrel.
  • Migrate all in-repo consumers of <kol-link-wc> to LinkFC + locally-managed LinkController instances, and update snapshots accordingly.

Reviewed changes

Copilot reviewed 50 out of 81 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
internal/functional-components/link/controller.ts New LinkController, createLinkStateAccess, click/tooltip wiring, location subscription.
internal/functional-components/link/component.tsx New LinkFC rendering the anchor + tooltip.
internal/functional-components/link/api.tsx New LinkApi and linkPropsConfig.
components/link/component.tsx KolLink now extends BaseWebComponent<LinkApi>; watchers delegate to controller; validateAccessAndShortKey no longer called.
components/link-button/shadow.tsx Migrated to LinkController; click handler diverges from controller's handleAnchorClick.
components/nav/shadow.tsx Per-entry LinkController map keyed by entry object; uses LinkFC.
components/toolbar/shadow.tsx Lazy controller creation inside render; _variant: 'normal' no longer reaches the controller.
components/tree-item/component.tsx, breadcrumb/shadow.tsx, skip-nav/shadow.tsx, form/shadow.tsx, table-stateless/component.tsx Migrated from KolLinkWcTag to LinkFC + local LinkController.
internal/props/*.ts (many new files) New per-prop definitions (access-key, aria-*, custom-class, disabled, download, inline, link-callbacks, link-label, link-role, short-key, tab-index, target, tooltip-align, variant).
internal/props/index.ts Re-exports the new prop modules.
Snapshots under components/*/__snapshots__/ Updated to reflect rendering through LinkFC (e.g. missing tabindex="0", missing kol-link--normal).
Comments suppressed due to low confidence (2)

packages/components/src/internal/functional-components/link/controller.ts:89

  • handleAnchorClick now returns shouldDispatchKolEvent: event.defaultPrevented === false, and kol-link's click handler only dispatches KolEvent.click when that flag is true. Previously, KolEvent.click was always dispatched when the component was not disabled, regardless of whether the onClick callback (or other listeners) called event.preventDefault(). This is a behavioral change for consumers that listen for KolEvent.click while also providing an onClick callback that may call preventDefault() (e.g. to suppress navigation). Additionally, kol-link-button has its own click handler in shadow.tsx that does not perform the defaultPrevented check, so the two components now diverge in behavior. Please confirm this change is intentional and align the two handlers.
    packages/components/src/components/toolbar/shadow.tsx:82
  • initLinkControllerFromProps is called with the raw toolbar item (element) but the surrounding code overrides _variant: 'normal' and _tabIndex: tabIndex on the props object that previously was spread onto KolLinkWcTag. Those overrides are now lost on the controller: _variant is taken from the original item (likely undefined) instead of 'normal', and tabIndex from the prop bag is unused for link items (only the explicit tabIndex={tabIndex} prop on LinkFC ties them together). The previous KolLinkWcTag {...props} {...element} spread set _variant="normal" on the rendered link. After this change, the kol-link--normal modifier class is no longer added; the snapshot now only shows class="button kol-link kol-link--inline kol-toolbar__item normal" (the literal "normal" from the host class, not kol-link--normal). If the styling of normal toolbar link items relies on .kol-link--normal, this is a visual regression.
			if (!ctrl) {
				ctrl = new LinkController(createLinkStateAccess(this.forceRender));
				initLinkControllerFromProps(ctrl, element as { _href: string } & Partial<Record<string, unknown>>);
				this.toolbarLinkCtrls.set(index, ctrl);
			}
			const linkCtrl = ctrl;

Comment thread packages/components/src/components/link/component.tsx Outdated
Comment thread packages/components/src/components/toolbar/shadow.tsx Outdated
Comment thread packages/components/src/components/nav/shadow.tsx
@deleonio
deleonio force-pushed the claude/migrate-kollink-skeleton-W6i6v branch 2 times, most recently from 225429c to 28317b9 Compare May 19, 2026 20:58
@deleonio
deleonio requested a review from Copilot May 19, 2026 21:20

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 50 out of 81 changed files in this pull request and generated no new comments.

@deleonio
deleonio marked this pull request as ready for review May 19, 2026 21:32

Copy link
Copy Markdown
Contributor Author

Visual Test Failures — Expected

The theme-desy and theme-kern visual tests are failing by design. The CI workflow generates fresh screenshots from the develop baseline and compares against this PR. The differences are intentional improvements from the LinkFC migration:

Root cause: toolbar, nav, and tree-item previously used <kol-link-wc> shadow DOM wrappers. Theme CSS rules for .kol-link inside toolbar.scss, nav.scss, etc. had no effect (shadow DOM boundary blocked them). With LinkFC, <a class="kol-link"> is rendered directly in the consuming component's shadow DOM, so those theme rules now actually apply.

Specific changes visible in desy/kern diffs:

  • Toolbar/nav/tree links now get --a11y-min-size: 36px from the desy button mixin (previously blocked by shadow DOM)
  • Kern kol-link--normal button styles (colors, borders) now apply correctly
  • .kol-toolbar .kol-link { border: 0 } and similar rules that were dead code now take effect

These are improvements, not regressions — the theme CSS was already written to style .kol-link in these contexts; it just wasn't reachable before.

Updated snapshots were committed in 495f72d6 on this branch to document the new correct appearance.

To resolve after merge: run the Update Snapshots workflow (workflow_dispatch) on develop to update the baseline.


Generated by Claude Code

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Es werden die leeren Icon tags gerendert bei den Navi entries ohne Iccons. Dadurch verschieben sich diese um das gap nach rechts. Siehe zB " 2 Navigation point"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Beim einklappen bleibt bei Navipunkten die ein a-tag sind das label stehen.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Das Icon des Smartbuttons fehlt hier komlett. Statt der class für das Icon wird undefined ausgegeben.

@BF150
BF150 force-pushed the claude/migrate-kollink-skeleton-W6i6v branch 4 times, most recently from d460ace to 82e15bf Compare June 29, 2026 10:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release:ignore PR wird vom Release-Changelog ausgeschlossen.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Skeleton - Link Skeleton - Button

5 participants