-
Notifications
You must be signed in to change notification settings - Fork 13
feat: add NumberField component #698
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| 'use client'; | ||
|
|
||
| import { Flex, NumberField, Text } from '@raystack/apsara'; | ||
| import PlaygroundLayout from './playground-layout'; | ||
|
|
||
| export function NumberFieldExamples() { | ||
| return ( | ||
| <PlaygroundLayout title='Number Field'> | ||
| <Flex direction='column' gap='large'> | ||
| <Text>Default:</Text> | ||
| <NumberField defaultValue={0} /> | ||
|
|
||
| <Text>With Min/Max (0-10):</Text> | ||
| <NumberField defaultValue={5} min={0} max={10} /> | ||
|
|
||
| <Text>With Step (5):</Text> | ||
| <NumberField defaultValue={0} step={5} /> | ||
|
|
||
| <Text>Disabled:</Text> | ||
| <NumberField defaultValue={0} disabled /> | ||
|
|
||
| <Text>Composed with ScrubArea:</Text> | ||
| <NumberField defaultValue={50}> | ||
| <NumberField.ScrubArea label='Amount' /> | ||
| <NumberField.Group> | ||
| <NumberField.Decrement /> | ||
| <NumberField.Input /> | ||
| <NumberField.Increment /> | ||
| </NumberField.Group> | ||
| </NumberField> | ||
| </Flex> | ||
| </PlaygroundLayout> | ||
| ); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| 'use client'; | ||
|
|
||
| import { getPropsString } from '@/lib/utils'; | ||
|
|
||
| export const getCode = (props: any) => { | ||
| return `<NumberField${getPropsString(props)}/>`; | ||
| }; | ||
|
|
||
| export const playground = { | ||
| type: 'playground', | ||
| controls: { | ||
| defaultValue: { type: 'text', initialValue: '0' }, | ||
| min: { type: 'text', initialValue: '' }, | ||
| max: { type: 'text', initialValue: '' }, | ||
| step: { type: 'text', initialValue: '1' }, | ||
| disabled: { type: 'checkbox', initialValue: false, defaultValue: false } | ||
| }, | ||
| getCode | ||
| }; | ||
|
|
||
| export const basicDemo = { | ||
| type: 'code', | ||
| code: `<NumberField defaultValue={0} />` | ||
| }; | ||
|
|
||
| export const minMaxDemo = { | ||
| type: 'code', | ||
| code: `<NumberField defaultValue={5} min={0} max={10} />` | ||
| }; | ||
|
|
||
| export const stepDemo = { | ||
| type: 'code', | ||
| code: `<NumberField defaultValue={0} step={5} />` | ||
| }; | ||
|
|
||
| export const disabledDemo = { | ||
| type: 'code', | ||
| code: `<NumberField defaultValue={0} disabled />` | ||
| }; | ||
|
|
||
| export const composedDemo = { | ||
| type: 'code', | ||
| code: `<NumberField defaultValue={0}> | ||
| <NumberField.ScrubArea label="Amount" /> | ||
| <NumberField.Group> | ||
| <NumberField.Decrement /> | ||
| <NumberField.Input /> | ||
| <NumberField.Increment /> | ||
| </NumberField.Group> | ||
| </NumberField>` | ||
| }; | ||
|
|
||
| export const formatDemo = { | ||
| type: 'code', | ||
| code: `<NumberField | ||
| defaultValue={1000} | ||
| format={{ style: 'currency', currency: 'USD' }} | ||
| />` | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,123 @@ | ||
| --- | ||
| title: Number Field | ||
| description: A numeric input component with increment and decrement buttons, supporting scrub interaction. | ||
| source: packages/raystack/components/number-field | ||
| tag: new | ||
| --- | ||
|
|
||
| import { | ||
| playground, | ||
| basicDemo, | ||
| minMaxDemo, | ||
| stepDemo, | ||
| disabledDemo, | ||
| composedDemo, | ||
| formatDemo, | ||
| } from "./demo.ts"; | ||
|
|
||
| <Demo data={playground} /> | ||
|
|
||
| ## Anatomy | ||
|
|
||
| Import and use the component standalone or composed: | ||
|
|
||
| ```tsx | ||
| import { NumberField } from "@raystack/apsara"; | ||
|
|
||
| {/* Standalone — renders decrement, input, and increment internally */} | ||
| <NumberField defaultValue={0} /> | ||
|
|
||
| {/* Composed — full control over sub-components */} | ||
| <NumberField defaultValue={0}> | ||
| <NumberField.ScrubArea label="Amount" /> | ||
| <NumberField.Group> | ||
| <NumberField.Decrement /> | ||
| <NumberField.Input /> | ||
| <NumberField.Increment /> | ||
| </NumberField.Group> | ||
| </NumberField> | ||
| ``` | ||
|
|
||
| ## API Reference | ||
|
|
||
| ### Root | ||
|
|
||
| The root component that manages numeric state. When no children are provided, it renders a default group with decrement, input, and increment controls. | ||
|
|
||
| <auto-type-table path="./props.ts" name="NumberFieldProps" /> | ||
|
|
||
| ### Group | ||
|
|
||
| Groups the input and button controls together. | ||
|
|
||
| <auto-type-table path="./props.ts" name="NumberFieldGroupProps" /> | ||
|
|
||
| ### Input | ||
|
|
||
| The numeric input element. | ||
|
|
||
| <auto-type-table path="./props.ts" name="NumberFieldInputProps" /> | ||
|
|
||
| ### Decrement | ||
|
|
||
| Button to decrease the value. | ||
|
|
||
| <auto-type-table path="./props.ts" name="NumberFieldDecrementProps" /> | ||
|
|
||
| ### Increment | ||
|
|
||
| Button to increase the value. | ||
|
|
||
| <auto-type-table path="./props.ts" name="NumberFieldIncrementProps" /> | ||
|
|
||
| ### ScrubArea | ||
|
|
||
| An interactive area that allows adjusting the value by dragging. Renders a Label component internally. | ||
|
|
||
| <auto-type-table path="./props.ts" name="NumberFieldScrubAreaProps" /> | ||
|
|
||
| ## Examples | ||
|
|
||
| ### Basic | ||
|
|
||
| A standalone number field with default controls. | ||
|
|
||
| <Demo data={basicDemo} /> | ||
|
|
||
| ### Min / Max | ||
|
|
||
| Number field constrained to a range. | ||
|
|
||
| <Demo data={minMaxDemo} /> | ||
|
|
||
| ### Step | ||
|
|
||
| Number field with a custom step amount. | ||
|
|
||
| <Demo data={stepDemo} /> | ||
|
|
||
| ### Disabled | ||
|
|
||
| Number field in disabled state. | ||
|
|
||
| <Demo data={disabledDemo} /> | ||
|
|
||
| ### Composed with ScrubArea | ||
|
|
||
| Full control with scrub area for drag-to-adjust interaction. | ||
|
|
||
| <Demo data={composedDemo} /> | ||
|
|
||
| ### Formatted | ||
|
|
||
| Number field with currency formatting. | ||
|
|
||
| <Demo data={formatDemo} /> | ||
|
|
||
| ## Accessibility | ||
|
|
||
| - The input has `role="textbox"` with `aria-roledescription="Number field"` | ||
| - Increment and decrement buttons are keyboard accessible | ||
| - ScrubArea associates its label with the input via `htmlFor` | ||
| - Supports `aria-valuemin`, `aria-valuemax`, and `aria-valuenow` | ||
| - Keyboard: Arrow Up/Down to increment/decrement, Shift for large step, Meta for small step | ||
|
Comment on lines
+117
to
+123
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Check the actual role and ARIA attributes in the component implementation
rg -n "role|aria-" packages/raystack/components/number-field/
# Check tests to see what role is asserted
rg -n "getByRole" packages/raystack/components/number-field/__tests__/Repository: raystack/apsara Length of output: 1332 🏁 Script executed: #!/bin/bash
# Search for aria-value attributes specifically
rg -n "aria-value" packages/raystack/components/number-field/
# Also check the entire number-field component file
wc -l packages/raystack/components/number-field/number-field.tsxRepository: raystack/apsara Length of output: 121 🏁 Script executed: cat -n packages/raystack/components/number-field/number-field.tsxRepository: raystack/apsara Length of output: 5531 🏁 Script executed: # Check the MDX file to see the exact content at lines 117-123
sed -n '117,123p' apps/www/src/content/docs/components/number-field/index.mdxRepository: raystack/apsara Length of output: 429 🏁 Script executed: # Check if aria-roledescription is mentioned anywhere in the component
rg -n "aria-roledescription" packages/raystack/components/number-field/
# Check Base UI documentation or types to see what ARIA attributes are supported
rg -n "aria-roledescription" --type ts --type tsxRepository: raystack/apsara Length of output: 86 🏁 Script executed: # Check if aria-roledescription is mentioned anywhere in the component
rg -n "aria-roledescription"Repository: raystack/apsara Length of output: 198 Remove unsupported ARIA attributes from accessibility documentation. The documentation claims the input supports 🤖 Prompt for AI Agents |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,89 @@ | ||
| export interface NumberFieldProps { | ||
| /** Controlled numeric value. */ | ||
| value?: number | null; | ||
|
|
||
| /** | ||
| * Initial uncontrolled value. | ||
| * @defaultValue 0 | ||
| */ | ||
| defaultValue?: number; | ||
|
|
||
| /** Event handler called when the value changes. */ | ||
| onValueChange?: (value: number | null, event: Event) => void; | ||
|
|
||
| /** Minimum allowed value. */ | ||
| min?: number; | ||
|
|
||
| /** Maximum allowed value. */ | ||
| max?: number; | ||
|
|
||
| /** | ||
| * Step amount for increment/decrement. | ||
| * @defaultValue 1 | ||
| */ | ||
| step?: number; | ||
|
|
||
| /** | ||
| * Whether the field is disabled. | ||
| * @defaultValue false | ||
| */ | ||
| disabled?: boolean; | ||
|
|
||
| /** | ||
| * Whether the field is read-only. | ||
| * @defaultValue false | ||
| */ | ||
| readOnly?: boolean; | ||
|
|
||
| /** | ||
| * Whether the field is required. | ||
| * @defaultValue false | ||
| */ | ||
| required?: boolean; | ||
|
|
||
| /** Number formatting options (Intl.NumberFormatOptions). */ | ||
| format?: Intl.NumberFormatOptions; | ||
|
|
||
| /** Additional CSS class names. */ | ||
| className?: string; | ||
| } | ||
|
|
||
| export interface NumberFieldGroupProps { | ||
| /** Additional CSS class names. */ | ||
| className?: string; | ||
| } | ||
|
|
||
| export interface NumberFieldInputProps { | ||
| /** Additional CSS class names. */ | ||
| className?: string; | ||
| } | ||
|
|
||
| export interface NumberFieldDecrementProps { | ||
| /** Custom content for the decrement button. Defaults to a minus icon. */ | ||
| children?: React.ReactNode; | ||
|
|
||
| /** Additional CSS class names. */ | ||
| className?: string; | ||
| } | ||
|
|
||
| export interface NumberFieldIncrementProps { | ||
| /** Custom content for the increment button. Defaults to a plus icon. */ | ||
| children?: React.ReactNode; | ||
|
|
||
| /** Additional CSS class names. */ | ||
| className?: string; | ||
| } | ||
|
|
||
| export interface NumberFieldScrubAreaProps { | ||
| /** Label text displayed in the scrub area. */ | ||
| label: string; | ||
|
|
||
| /** | ||
| * Scrub direction. | ||
| * @defaultValue "horizontal" | ||
| */ | ||
| direction?: 'horizontal' | 'vertical'; | ||
|
|
||
| /** Additional CSS class names. */ | ||
| className?: string; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
getCodesignature does not match the requiredGetCodeTypeinterface.According to
apps/www/src/components/demo/types.ts,GetCodeTyperequires two parameters:(updatedProps: Record<string, any>, props: Record<string, any>). The current implementation only accepts one parameter, which will cause the playground to malfunction when generating code snippets.🐛 Proposed fix
📝 Committable suggestion
🤖 Prompt for AI Agents