Skip to content

feat(svelte-ds-app-launchpad): upstream TextInput, NumberInput, Textarea, and InputPrimitive components#441

Open
goulinkh wants to merge 4 commits intomainfrom
feat/upstream-textinput-textarea-components
Open

feat(svelte-ds-app-launchpad): upstream TextInput, NumberInput, Textarea, and InputPrimitive components#441
goulinkh wants to merge 4 commits intomainfrom
feat/upstream-textinput-textarea-components

Conversation

@goulinkh
Copy link
Contributor

Fixes LP-3722

PR readiness check

  • PR should have one of the following labels:
    • Feature 🎁, Breaking Change 💣, Bug 🐛, Documentation 📝, Maintenance 🔨.
  • PR title follows the Conventional Commits format.
  • All packages define the required scripts in package.json:
    • All packages: check, check:fix, and test.
    • Packages with build steps: build to build the package for development or distribution, build:all to build all artifacts. See CONTRIBUTING.md for details.

Screenshots

image

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Upstreams new form input components into the ds-app-launchpad Svelte design system package, adding a TextInput wrapper, a Textarea with optional dynamic row sizing, and a shared TextInputPrimitive, along with Storybook stories and Vitest coverage.

Changes:

  • Export TextInput/Textarea from the public components barrel and TextInputPrimitive from components/common.
  • Add TextInputPrimitive, TextInput, and Textarea implementations + styles + Storybook stories.
  • Add client + SSR tests for the new components, and unit tests for calculateDynamicRows; update package test script to run the additional Vitest project.

Reviewed changes

Copilot reviewed 27 out of 27 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
packages/svelte/ds-app-launchpad/src/lib/components/index.ts Re-exports TextInput and Textarea from the package component barrel.
packages/svelte/ds-app-launchpad/src/lib/components/common/index.ts Re-exports TextInputPrimitive from the common components barrel.
packages/svelte/ds-app-launchpad/src/lib/components/common/TextInputPrimitive/types.ts Defines prop typing for the input primitive.
packages/svelte/ds-app-launchpad/src/lib/components/common/TextInputPrimitive/styles.css Base styling for the input primitive.
packages/svelte/ds-app-launchpad/src/lib/components/common/TextInputPrimitive/index.ts Public entrypoint for TextInputPrimitive.
packages/svelte/ds-app-launchpad/src/lib/components/common/TextInputPrimitive/TextInputPrimitive.svelte.test.ts Browser tests for the input primitive.
packages/svelte/ds-app-launchpad/src/lib/components/common/TextInputPrimitive/TextInputPrimitive.svelte Implements the primitive input (bindable value/ref).
packages/svelte/ds-app-launchpad/src/lib/components/common/TextInputPrimitive/TextInputPrimitive.stories.svelte Storybook stories for the input primitive.
packages/svelte/ds-app-launchpad/src/lib/components/common/TextInputPrimitive/TextInputPrimitive.ssr.test.ts SSR tests for the input primitive.
packages/svelte/ds-app-launchpad/src/lib/components/Textarea/utils/index.ts Barrel export for textarea utilities.
packages/svelte/ds-app-launchpad/src/lib/components/Textarea/utils/calculateDynamicRows.ts Adds dynamic row calculation helper for textarea.
packages/svelte/ds-app-launchpad/src/lib/components/Textarea/utils/calculateDynamicRows.test.ts Unit tests for the dynamic rows helper.
packages/svelte/ds-app-launchpad/src/lib/components/Textarea/types.ts Defines Textarea props including static/dynamic rows.
packages/svelte/ds-app-launchpad/src/lib/components/Textarea/styles.css Styling for Textarea.
packages/svelte/ds-app-launchpad/src/lib/components/Textarea/index.ts Public entrypoint for Textarea.
packages/svelte/ds-app-launchpad/src/lib/components/Textarea/Textarea.svelte.test.ts Browser tests for Textarea (incl. dynamic rows behavior).
packages/svelte/ds-app-launchpad/src/lib/components/Textarea/Textarea.svelte Implements Textarea with derived rows behavior and bindable value/ref.
packages/svelte/ds-app-launchpad/src/lib/components/Textarea/Textarea.stories.svelte Storybook stories for Textarea (static + dynamic rows).
packages/svelte/ds-app-launchpad/src/lib/components/Textarea/Textarea.ssr.test.ts SSR tests for Textarea.
packages/svelte/ds-app-launchpad/src/lib/components/TextInput/types.ts Defines TextInput props including severity/density modifiers.
packages/svelte/ds-app-launchpad/src/lib/components/TextInput/styles.css Styling overrides for TextInput on top of the primitive.
packages/svelte/ds-app-launchpad/src/lib/components/TextInput/index.ts Public entrypoint for TextInput.
packages/svelte/ds-app-launchpad/src/lib/components/TextInput/TextInput.svelte.test.ts Browser tests for TextInput.
packages/svelte/ds-app-launchpad/src/lib/components/TextInput/TextInput.svelte Implements TextInput as a styled wrapper around TextInputPrimitive.
packages/svelte/ds-app-launchpad/src/lib/components/TextInput/TextInput.stories.svelte Storybook stories for TextInput incl. severities.
packages/svelte/ds-app-launchpad/src/lib/components/TextInput/TextInput.ssr.test.ts SSR tests for TextInput.
packages/svelte/ds-app-launchpad/package.json Updates the test script to run server tests in addition to ssr.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

*/
value?: string | number;

density?: "dense" | "medium";
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

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

density is typed as "dense" | "medium", but this package’s modifier-families define a compact density as well. Since the component forwards density directly as a class (and modifier-families/styles/density.css contains .compact), consumers should be able to pass "compact" too. Consider typing this via ModifierFamily<['density','severity']> (or ModifierFamilyValues['density']) to stay consistent with other components (e.g. Chip/Button) and avoid drifting values.

Suggested change
density?: "dense" | "medium";
density?: "compact" | "dense" | "medium";

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

the spec of inputs only has two densities, this is intentional:
image

Comment on lines 11 to 21
const linesCount = countLinesToLimit(
textareaValue,
Math.max(minRows, maxRows),
);

if (linesCount <= minRows) {
return minRows;
} else if (linesCount <= maxRows) {
return linesCount;
} else {
return maxRows;
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

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

calculateDynamicRows can behave incorrectly when minRows > maxRows (which isn’t prevented by the rows tuple type). In that case linesCount is computed against Math.max(minRows, maxRows) but the function may still return the smaller maxRows, causing rows to shrink unexpectedly. Consider normalizing/validating the inputs (e.g., swap values when out of order or throw) and using the validated maxRows as the counting limit.

Suggested change
const linesCount = countLinesToLimit(
textareaValue,
Math.max(minRows, maxRows),
);
if (linesCount <= minRows) {
return minRows;
} else if (linesCount <= maxRows) {
return linesCount;
} else {
return maxRows;
const normalizedMinRows = Math.min(minRows, maxRows);
const normalizedMaxRows = Math.max(minRows, maxRows);
const linesCount = countLinesToLimit(
textareaValue,
normalizedMaxRows,
);
if (linesCount <= normalizedMinRows) {
return normalizedMinRows;
} else if (linesCount <= normalizedMaxRows) {
return linesCount;
} else {
return normalizedMaxRows;

Copilot uses AI. Check for mistakes.
Copy link
Member

Choose a reason for hiding this comment

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

This makes sense, I'd consider throwing n error when minRows > maxRows

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have added a check to swap values in case the couple values are not in the proper order.

export interface TextInputPrimitiveProps
extends Omit<HTMLInputAttributes, "children"> {
ref?: HTMLInputElement;
type?: "text" | "password" | "email" | "url" | "tel" | "search";
Copy link
Member

Choose a reason for hiding this comment

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

Is type necessary here? Isn't it already part of HTMLInputAttributes?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This has been moved to InputPrimitive, type excludes number. I have rewritten this is a more readable way.

Copy link
Member

Choose a reason for hiding this comment

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

There's only story coverage for type="password", it would be good to cover the other possible cases (either in a "types" story or in one story per type, like you've done with password

Copy link
Contributor Author

Choose a reason for hiding this comment

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

add some other stories in components/TextInput

/**
* The type of input control to display.
*/
type?: "text" | "password" | "email" | "url" | "tel" | "search";
Copy link
Member

Choose a reason for hiding this comment

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

Like my other comment, can't we use type from HTMLInputAttributes without adding it explicitly here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done in InputPrimitive

import type { HTMLInputAttributes } from "svelte/elements";
import type { ModifierFamily } from "modifier-families";

export interface TextInputProps
Copy link
Member

Choose a reason for hiding this comment

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

TextInput spreads its props onto TextInputPrimitive, but there is no explicit relationship between their props. I think TextInputProps should extend TextInputPrimitiveProps - otherwise it is much easier for props we don't expect to be passed to one or both of these components.

@advl
Copy link
Contributor

advl commented Feb 27, 2026

I would like to attract your attention that it is becoming important to document the code standards for svelte going forward. This will facilitate reviews and shipping code.

@goulinkh goulinkh force-pushed the feat/upstream-textinput-textarea-components branch from 6f97944 to 2602023 Compare February 27, 2026 15:38
@goulinkh
Copy link
Contributor Author

Hi @jmuzina, I have updated the PR addressing your comments.
I have missed a recent merge that was added by @steciuk which consists of introducing the number input component, here are the changes this introduced:

  • TextInputPrimitive becomes more generic InputPrimitive
  • NumberInput component

Sorry for missing this with the initial PR draft!

@goulinkh
Copy link
Contributor Author

goulinkh commented Feb 27, 2026

I will let @steciuk take it from here, since I will be changing my focus next pulse on other topics.

@goulinkh goulinkh changed the title feat(svelte-ds-app-launchpad): upstream TextInput, Textarea, and TextInputPrimitive components feat(svelte-ds-app-launchpad): upstream TextInput, NumberInput, Textarea, and InputPrimitive components Feb 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants