Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/tonic-ui-form-control.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@tonic-ui/react": minor
---

feat: Add `FormControl` components with comprehensive accessibility support
2 changes: 1 addition & 1 deletion packages/react-docs/config/sidebar-routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,6 @@ export const routes = [
{ title: 'Getting started', path: 'experiments' },

{ title: 'FORM CONTROLS', heading: true },
{ title: 'FormControl', path: 'experiments/form-control' },
{ title: 'ButtonBox', path: 'experiments/button-box' },
{ title: 'Dropdown', path: 'experiments/dropdown' },
{ title: 'DropdownBase', path: 'experiments/dropdown-base' },
Expand Down Expand Up @@ -222,6 +221,7 @@ export const routes = [
},
},
{ title: 'CheckboxGroup', path: 'components/checkbox-group' },
{ title: 'FormControl', path: 'components/form-control' },
{
title: 'Input',
path: 'components/input',
Expand Down
39 changes: 0 additions & 39 deletions packages/react-docs/experiments/form-control/FormCharacterCount.js

This file was deleted.

55 changes: 0 additions & 55 deletions packages/react-docs/experiments/form-control/FormControl.js

This file was deleted.

47 changes: 0 additions & 47 deletions packages/react-docs/experiments/form-control/FormInput.js

This file was deleted.

17 changes: 0 additions & 17 deletions packages/react-docs/experiments/form-control/index.js

This file was deleted.

21 changes: 21 additions & 0 deletions packages/react-docs/pages/components/form-control/basic.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React from 'react';
import {
FormControl,
FormLabel,
FormInput,
FormHelperText,
} from '@tonic-ui/react';

const App = () => {
return (
<FormControl>
<FormLabel>Username</FormLabel>
<FormInput placeholder="Enter your username" />
<FormHelperText>
Choose a unique username that others will see
</FormHelperText>
</FormControl>
);
};

export default App;
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import React, { useState } from 'react';
import {
FormControl,
FormLabel,
FormInput,
FormCharacterCount,
} from '@tonic-ui/react';

const App = () => {
const [bio, setBio] = useState('');
const maxChars = 50;

return (
<FormControl>
<FormLabel>Bio</FormLabel>
<FormInput
placeholder="Tell us about yourself"
value={bio}
onChange={(e) => setBio(e.target.value)}
/>
<FormCharacterCount count={bio.length} maxCount={maxChars} />
</FormControl>
);
};

export default App;
Original file line number Diff line number Diff line change
@@ -1,18 +1,26 @@
import React, { useState } from 'react';
import { ensureArray } from 'ensure-type';
import { Box, Stack, Button, Text } from '@tonic-ui/react';
import {
Box,
Stack,
Button,
Text,
Flex,
FormControl,
FormInput,
FormLabel,
FormInput,
FormTextarea,
FormErrorMessage,
FormHelperText,
} from '@/experiments/form-control';
FormCharacterCount,
} from '@tonic-ui/react';

const App = () => {
const [formData, setFormData] = useState({
email: '',
password: '',
country: '',
bio: '',
});

const [errors, setErrors] = useState({});
Expand All @@ -39,6 +47,19 @@ const App = () => {
}
return passwordErrors;
}
case 'country': {
return !value ? 'Please select your experience level' : '';
}
case 'bio': {
const bioErrors = [];
if (value.length < 10) {
bioErrors.push('Bio must be at least 10 characters');
}
if (value.length > 200) {
bioErrors.push('Bio must not exceed 200 characters');
}
return bioErrors;
}
default:
return '';
}
Expand Down Expand Up @@ -135,6 +156,26 @@ const App = () => {
</FormHelperText>
</FormControl>

{/* Bio textarea with character validation */}
<FormControl error={hasError('bio')}>
<FormLabel>Bio</FormLabel>
<FormTextarea
name="bio"
value={formData.bio}
onChange={handleChange}
onBlur={handleBlur}
placeholder="Tell us about yourself..."
rows={4}
/>
<FormErrorMessage errors={ensureArray(errors.bio)} />
<Flex justifyContent="space-between">
<FormHelperText>
Write a brief bio (10-200 characters)
</FormHelperText>
<FormCharacterCount count={formData.bio.length} maxCount={200} />
</Flex>
</FormControl>

{/* Submit Button */}
<Button
type="submit"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import React from 'react';
import {
Stack,
Flex,
Text,
Tooltip,
FormControl,
FormLabel,
FormInput,
FormErrorMessage,
} from '@tonic-ui/react';
import { InfoOIcon } from '@tonic-ui/react-icons';

// InfoTip component using Tooltip
const InfoTip = ({ label }) => {
return (
<Tooltip label={label} maxWidth={320}>
<InfoOIcon
color="gray:50"
_hover={{
color: 'gray:60',
}}
/>
</Tooltip>
);
};

const App = () => {
return (
<Stack direction="column" spacing="4x">
{/* Custom required indicator with text */}
<FormControl gap="1x">
<Flex>
<FormLabel>Email</FormLabel>
<Text color="red:50" fontSize="xs" ml="2x">
(optional)
</Text>
</Flex>
<FormInput placeholder="Custom text indicator" />
</FormControl>

{/* Label with InfoTip */}
<FormControl gap="1x">
<Flex alignItems="center" gap="2x">
<FormLabel required>API Key</FormLabel>
<InfoTip label="Your API key is used to authenticate requests. Keep it secure and don't share it publicly." />
</Flex>
<FormInput placeholder="Enter your API key" />
</FormControl>

{/* Error message with InfoTip */}
<FormControl error gap="1x">
<FormLabel required>Password</FormLabel>
<FormInput type="password" placeholder="Enter password" />
<FormErrorMessage
errors={[
<Flex key="password-length" alignItems="center" gap="2x">
<Text>Password must contain at least 8 characters</Text>
<InfoTip label="Use a mix of uppercase, lowercase, numbers, and special characters for better security." />
</Flex>,
'Must include at least one uppercase letter',
<Flex key="password-number" alignItems="center" gap="2x">
<Text>Must include at least one number</Text>
<InfoTip label="Numbers help make your password more secure against common attacks." />
</Flex>,
]}
/>
</FormControl>
</Stack>
);
};

export default App;
Loading
Loading