-
-
Notifications
You must be signed in to change notification settings - Fork 33
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
TextField component #511
base: main
Are you sure you want to change the base?
TextField component #511
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,28 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import React from 'react'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import TextFieldRoot from './fragments/TextFieldRoot'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import TextFieldInput from './fragments/TextFieldInput'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import TextFieldSlot from './fragments/TextFieldSlot'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
type TextFieldProps = React.InputHTMLAttributes<HTMLInputElement> & { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
className?: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
customRootClass?: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const TextField = ({ className = '', customRootClass = '', value, onChange, ...props }: TextFieldProps) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<TextFieldRoot customRootClass={customRootClass} className={className}> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<TextFieldSlot> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
slot 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</TextFieldSlot> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{/* */} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<TextFieldInput type="text" value={value} onChange={onChange} {...props} /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{/* */} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<TextFieldSlot> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
slot 2 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</TextFieldSlot> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</TextFieldRoot> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+12
to
+26
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. Component implementation needs significant improvements. Several issues need to be addressed:
Here's a suggested improvement: -const TextField = ({ className = '', customRootClass = '', value, onChange, ...props }: TextFieldProps) => {
+const TextField = ({
+ className = '',
+ customRootClass = '',
+ value,
+ onChange,
+ 'aria-label': ariaLabel,
+ ...props
+}: TextFieldProps) => {
return (
- <TextFieldRoot customRootClass={customRootClass} className={className}>
- <TextFieldSlot>
- slot 1
- </TextFieldSlot>
- {/* */}
- <TextFieldInput type="text" value={value} onChange={onChange} {...props} />
- {/* */}
- <TextFieldSlot>
- slot 2
- </TextFieldSlot>
+ <TextFieldRoot customRootClass={customRootClass} className={className} role="group">
+ {props.children || (
+ <>
+ <TextFieldSlot />
+ <TextFieldInput
+ type="text"
+ value={value}
+ onChange={onChange}
+ aria-label={ariaLabel}
+ {...props}
+ />
+ <TextFieldSlot />
+ </>
+ )}
</TextFieldRoot>
);
}; 📝 Committable suggestion
Suggested change
💡 Codebase verification Add basic input validation and error handling to TextField component The TextField component currently lacks input validation and error handling. Based on the codebase analysis:
Suggested improvements:
🔗 Analysis chainAdd error handling and validation. The component should handle common error cases and validate props appropriately. Let's check if there are any existing error handling patterns in the codebase: Let me check the error handling pattern found and look for any prop validation patterns in the codebase. Let me check the specific TextField interface and any existing validation patterns in similar input components. Let me try one final search to understand the component's interface and validation patterns. 🏁 Scripts executedThe following scripts were executed for the analysis: Script: #!/bin/bash
# Look for error handling patterns in other components
rg -g '*.tsx' -g '*.ts' 'try.*catch|throw.*Error|Error\(' src/components/
Length of output: 156 Script: #!/bin/bash
# Check the specific error handling in ProgressIndicator
rg -A 5 'throw new Error' src/components/ui/Progress/shards/ProgressIndicator.tsx
# Look for prop validation patterns, particularly with PropTypes or TypeScript
ast-grep --pattern 'interface $_Props {
$$$
}'
# Check for any validation utilities or helper functions
rg -g '*.ts' -g '*.tsx' 'validate|isValid|check' src/
Length of output: 1684 Script: #!/bin/bash
# Check TextField interface
rg -A 10 'interface TextFieldProps' src/
# Look for validation patterns in input-like components
rg -g '*.tsx' -A 10 'input.*value.*onChange' src/components/
# Check for any existing input validation examples
ast-grep --pattern 'const $_ = ({ value, onChange, $$ }) => {
$$$
}'
Length of output: 178 Script: #!/bin/bash
# Search for the TextField type definitions
fd "TextField.tsx" --exec cat {}
# Look for any input validation patterns in the entire codebase
rg -g '*.tsx' -g '*.ts' -B 2 -A 2 'onChange.*=.*\(.*\).*=>' src/
# Check for any prop validation utilities
fd -e ts -e tsx --exec grep -l "validate\|isValid" {} src/utils/
Length of output: 7164 |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
export default TextField; |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,7 @@ | ||||||||||||||||||||||||||||||||||||||||
import React from 'react'; | ||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||
const TextFieldInput = ({ className = '', ...props }: React.InputHTMLAttributes<HTMLInputElement>) => { | ||||||||||||||||||||||||||||||||||||||||
return <input className={`${className}`} {...props} />; | ||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||
Comment on lines
+3
to
+5
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. 🛠️ Refactor suggestion Consider integrating with the project's styling system. The className handling could be improved by integrating with the project's styling system mentioned in the summary (rad-ui-text-field). -const TextFieldInput = ({ className = '', ...props }: React.InputHTMLAttributes<HTMLInputElement>) => {
- return <input className={`${className}`} {...props} />;
+const TextFieldInput = ({ className = '', ...props }: React.InputHTMLAttributes<HTMLInputElement>) => {
+ return <input className={`rad-ui-text-field__input ${className}`} {...props} />; 📝 Committable suggestion
Suggested change
🛠️ Refactor suggestion Enhance accessibility with ARIA attributes. Consider adding appropriate ARIA attributes and role to improve accessibility. const TextFieldInput = ({ className = '', ...props }: React.InputHTMLAttributes<HTMLInputElement>) => {
- return <input className={`${className}`} {...props} />;
+ return (
+ <input
+ className={`${className}`}
+ role="textbox"
+ aria-label={props['aria-label'] || 'text input'}
+ {...props}
+ />
+ ); 📝 Committable suggestion
Suggested change
🛠️ Refactor suggestion Consider handling controlled component behavior. Since this is a form input component, it might be helpful to provide some built-in handling for controlled component patterns. -const TextFieldInput = ({ className = '', ...props }: React.InputHTMLAttributes<HTMLInputElement>) => {
- return <input className={`${className}`} {...props} />;
+const TextFieldInput = ({
+ className = '',
+ value,
+ defaultValue,
+ onChange,
+ ...props
+}: TextFieldInputProps) => {
+ const isControlled = value !== undefined;
+ return (
+ <input
+ className={`${className}`}
+ value={isControlled ? value : undefined}
+ defaultValue={!isControlled ? defaultValue : undefined}
+ onChange={(e) => {
+ onChange?.(e);
+ }}
+ {...props}
+ />
+ );
|
||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||
export default TextFieldInput; | ||||||||||||||||||||||||||||||||||||||||
Comment on lines
+1
to
+7
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. 🛠️ Refactor suggestion Export component type for better developer experience. Consider exporting the component's type definition to improve TypeScript integration. import React from 'react';
+export type TextFieldInputProps = React.InputHTMLAttributes<HTMLInputElement>;
+
-const TextFieldInput = ({ className = '', ...props }: React.InputHTMLAttributes<HTMLInputElement>) => {
+const TextFieldInput = ({ className = '', ...props }: TextFieldInputProps) => {
return <input className={`${className}`} {...props} />;
};
export default TextFieldInput; 📝 Committable suggestion
Suggested change
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import React from 'react'; | ||
|
||
import { customClassSwitcher } from '~/core'; | ||
|
||
const COMPONENT_NAME = 'TextField'; | ||
|
||
type TextFieldRootProps = React.HTMLAttributes<HTMLDivElement> & { | ||
className?: string; | ||
customRootClass?: string; | ||
}; | ||
|
||
const TextFieldRoot = ({ className = '', customRootClass = '', ...props }: TextFieldRootProps) => { | ||
const rootClass = customClassSwitcher(customRootClass, COMPONENT_NAME); | ||
return <div className={`${rootClass} ${className}`} {...props} />; | ||
}; | ||
Comment on lines
+12
to
+15
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. 🛠️ Refactor suggestion Consider using a class name utility library. The current class name concatenation could lead to extra spaces and doesn't handle falsy values. Consider using a utility like +import clsx from 'clsx';
+
const TextFieldRoot = ({ className = '', customRootClass = '', ...props }: TextFieldRootProps) => {
const rootClass = customClassSwitcher(customRootClass, COMPONENT_NAME);
- return <div className={`${rootClass} ${className}`} {...props} />;
+ return <div className={clsx(rootClass, className)} {...props} />;
};
|
||
|
||
TextFieldRoot.displayName = COMPONENT_NAME; | ||
|
||
export default TextFieldRoot; |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,7 @@ | ||||||||||||||||||||||||||||||||||||
import React from 'react'; | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
const TextFieldSlot = ({ children }: React.HTMLAttributes<HTMLDivElement>) => { | ||||||||||||||||||||||||||||||||||||
return <div className="rad-ui-text-field-slot">{children}</div>; | ||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||
Comment on lines
+3
to
+5
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. 🛠️ Refactor suggestion Improve component typing and props handling. The component could benefit from more precise typing and proper props handling:
Consider this improved implementation: -const TextFieldSlot = ({ children }: React.HTMLAttributes<HTMLDivElement>) => {
- return <div className="rad-ui-text-field-slot">{children}</div>;
+type TextFieldSlotProps = {
+ children: React.ReactNode;
+} & Omit<React.HTMLAttributes<HTMLDivElement>, 'children'>;
+
+const TextFieldSlot: React.FC<TextFieldSlotProps> = ({ children, className, ...props }) => {
+ return (
+ <div
+ className={['rad-ui-text-field-slot', className].filter(Boolean).join(' ')}
+ {...props}
+ >
+ {children}
+ </div>
+ );
}; This change:
📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
export default TextFieldSlot; |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,47 @@ | ||||||||||||||||||||||
import React from 'react'; | ||||||||||||||||||||||
import TextField from '../TextField'; | ||||||||||||||||||||||
import SandboxEditor from '~/components/tools/SandboxEditor/SandboxEditor'; | ||||||||||||||||||||||
|
||||||||||||||||||||||
import Button from '~/components/ui/Button/Button'; | ||||||||||||||||||||||
|
||||||||||||||||||||||
export default { | ||||||||||||||||||||||
title: 'Components/TextField', | ||||||||||||||||||||||
component: TextField, | ||||||||||||||||||||||
render: (args) => <Template {...args} /> | ||||||||||||||||||||||
}; | ||||||||||||||||||||||
|
||||||||||||||||||||||
const Template = (args) => { | ||||||||||||||||||||||
return <SandboxEditor className="space-y-4 pt-4"> | ||||||||||||||||||||||
<TextField {...args} /> | ||||||||||||||||||||||
</SandboxEditor>; | ||||||||||||||||||||||
}; | ||||||||||||||||||||||
|
||||||||||||||||||||||
// More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args | ||||||||||||||||||||||
export const All = { | ||||||||||||||||||||||
args: { | ||||||||||||||||||||||
className: '' | ||||||||||||||||||||||
} | ||||||||||||||||||||||
}; | ||||||||||||||||||||||
Comment on lines
+20
to
+24
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. 🛠️ Refactor suggestion Enhance the basic story to showcase component capabilities. The current story with an empty className doesn't effectively demonstrate the TextField's features. Consider expanding the story to show different states and use cases: export const All = {
args: {
- className: ''
+ placeholder: 'Enter text here',
+ defaultValue: 'Sample text',
+ disabled: false,
+ // Add other relevant props to showcase functionality
}
};
|
||||||||||||||||||||||
|
||||||||||||||||||||||
const WithFormTemplate = (args) => { | ||||||||||||||||||||||
const [value, setValue] = React.useState(''); | ||||||||||||||||||||||
const handleChange = (e) => { | ||||||||||||||||||||||
setValue(e.target.value); | ||||||||||||||||||||||
}; | ||||||||||||||||||||||
|
||||||||||||||||||||||
const handleSubmit = (e) => { | ||||||||||||||||||||||
e.preventDefault(); | ||||||||||||||||||||||
console.log(value); | ||||||||||||||||||||||
}; | ||||||||||||||||||||||
Comment on lines
+32
to
+35
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. 🛠️ Refactor suggestion Replace console.log with Storybook action. Using console.log for form submission isn't ideal in Storybook stories. +import { action } from '@storybook/addon-actions';
+
const handleSubmit = (e) => {
e.preventDefault();
- console.log(value);
+ action('form-submitted')(value);
}; 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||
|
||||||||||||||||||||||
return <SandboxEditor className="space-y-4 pt-4"> | ||||||||||||||||||||||
<form onSubmit={handleSubmit}> | ||||||||||||||||||||||
<TextField {...args} value={value} onChange={handleChange} /> | ||||||||||||||||||||||
<Button type="submit" onClick={handleSubmit}>Submit</Button> | ||||||||||||||||||||||
</form> | ||||||||||||||||||||||
</SandboxEditor>; | ||||||||||||||||||||||
}; | ||||||||||||||||||||||
|
||||||||||||||||||||||
export const WithForm = WithFormTemplate.bind({}); | ||||||||||||||||||||||
WithForm.args = { | ||||||||||||||||||||||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
.rad-ui-text-field{ | ||
display: flex; | ||
border: 1px solid var(--rad-ui-color-gray-1000); | ||
border-radius: 8px; | ||
padding: 8px 12px; | ||
} |
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.
🛠️ Refactor suggestion
Consider importing types from fragment components.
For better type safety and maintainability, consider importing and re-exporting the types from your fragment components. This would make the API surface more explicit and help with documentation.
📝 Committable suggestion