Skip to content

Commit de52048

Browse files
authored
Merge pull request #1 from Racks-Labs/feat/flags-number
feat: integrate PhoneInput component into form fields and OpenText el…
2 parents 0c28e89 + 70de072 commit de52048

7 files changed

Lines changed: 528 additions & 19 deletions

File tree

apps/web/modules/survey/editor/components/open-element-form.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ const getPlaceholderByInputType = (inputType: TSurveyOpenTextElementInputType) =
259259
case "number":
260260
return "42";
261261
case "phone":
262-
return "+1 123 456 789";
262+
return "123 456 789";
263263
default:
264264
return "Type your answer here...";
265265
}

packages/survey-ui/src/components/elements/form-field.tsx

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import * as React from "react";
2-
import { ElementError } from "@/components/general/element-error";
32
import { ElementHeader } from "@/components/general/element-header";
43
import { Input } from "@/components/general/input";
54
import { Label } from "@/components/general/label";
5+
import { PhoneInput } from "@/components/general/phone-input";
66

77
/**
88
* Form field configuration
@@ -97,7 +97,6 @@ function FormField({
9797

9898
{/* Form Fields */}
9999
<div className="relative space-y-3">
100-
<ElementError errorMessage={errorMessage} dir={dir} />
101100
{visibleFields.map((field) => {
102101
const fieldRequired = isFieldRequired(field);
103102
const fieldValue = currentValues[field.id] ?? "";
@@ -111,23 +110,40 @@ function FormField({
111110
inputType = "tel";
112111
}
113112

113+
const isPhoneField = field.id === "phone" || inputType === "tel";
114+
114115
return (
115116
<div key={field.id} className="space-y-2">
116117
<Label htmlFor={fieldInputId} variant="default">
117118
{fieldRequired ? `${field.label}*` : field.label}
118119
</Label>
119-
<Input
120-
id={fieldInputId}
121-
type={inputType}
122-
value={fieldValue}
123-
onChange={(e) => {
124-
handleFieldChange(field.id, e.target.value);
125-
}}
126-
required={fieldRequired}
127-
disabled={disabled}
128-
dir={dir}
129-
aria-invalid={Boolean(errorMessage) || undefined}
130-
/>
120+
{isPhoneField ? (
121+
<PhoneInput
122+
id={fieldInputId}
123+
value={fieldValue}
124+
onChange={(newValue) => {
125+
handleFieldChange(field.id, newValue);
126+
}}
127+
placeholder={field.placeholder}
128+
required={fieldRequired}
129+
disabled={disabled}
130+
dir={dir}
131+
errorMessage={errorMessage}
132+
/>
133+
) : (
134+
<Input
135+
id={fieldInputId}
136+
type={inputType}
137+
value={fieldValue}
138+
onChange={(e) => {
139+
handleFieldChange(field.id, e.target.value);
140+
}}
141+
required={fieldRequired}
142+
disabled={disabled}
143+
dir={dir}
144+
errorMessage={errorMessage}
145+
/>
146+
)}
131147
</div>
132148
);
133149
})}

packages/survey-ui/src/components/elements/open-text.tsx

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { useState } from "react";
2-
import { ElementError } from "@/components/general/element-error";
32
import { ElementHeader } from "@/components/general/element-header";
43
import { Input } from "@/components/general/input";
4+
import { PhoneInput } from "@/components/general/phone-input";
55
import { Textarea } from "@/components/general/textarea";
66
import { cn } from "@/lib/utils";
77

@@ -51,6 +51,13 @@ function OpenText({
5151
onChange(newValue);
5252
};
5353

54+
const handlePhoneChange = (newValue: string): void => {
55+
setCurrentLength(newValue.length);
56+
onChange(newValue);
57+
};
58+
59+
const isPhoneInput = inputType === "phone";
60+
5461
const renderCharLimit = (): React.JSX.Element | null => {
5562
if (charLimit?.max === undefined) return null;
5663
const isOverLimit = currentLength >= charLimit.max;
@@ -68,7 +75,6 @@ function OpenText({
6875

6976
{/* Input or Textarea */}
7077
<div className="relative space-y-2">
71-
<ElementError errorMessage={errorMessage} dir={dir} />
7278
<div className="space-y-1">
7379
{longAnswer ? (
7480
<Textarea
@@ -80,10 +86,21 @@ function OpenText({
8086
dir={dir}
8187
rows={rows}
8288
disabled={disabled}
83-
aria-invalid={Boolean(errorMessage) || undefined}
89+
errorMessage={errorMessage}
8490
minLength={charLimit?.min}
8591
maxLength={charLimit?.max}
8692
/>
93+
) : isPhoneInput ? (
94+
<PhoneInput
95+
id={inputId}
96+
placeholder={placeholder}
97+
value={value}
98+
onChange={handlePhoneChange}
99+
required={required}
100+
dir={dir}
101+
disabled={disabled}
102+
errorMessage={errorMessage}
103+
/>
87104
) : (
88105
<Input
89106
id={inputId}
@@ -94,7 +111,7 @@ function OpenText({
94111
required={required}
95112
dir={dir}
96113
disabled={disabled}
97-
aria-invalid={Boolean(errorMessage) || undefined}
114+
errorMessage={errorMessage}
98115
minLength={charLimit?.min}
99116
maxLength={charLimit?.max}
100117
/>
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import type { Meta, StoryObj } from "@storybook/react";
2+
import type { ComponentProps } from "react";
3+
import { useState } from "react";
4+
import { PhoneInput, type PhoneInputProps } from "./phone-input";
5+
6+
const meta: Meta<typeof PhoneInput> = {
7+
title: "General/PhoneInput",
8+
component: PhoneInput,
9+
tags: ["autodocs"],
10+
parameters: {
11+
layout: "centered",
12+
},
13+
decorators: [
14+
(Story: React.ComponentType) => (
15+
<div className="w-[350px]">
16+
<Story />
17+
</div>
18+
),
19+
],
20+
};
21+
22+
export default meta;
23+
24+
type Story = StoryObj<typeof PhoneInput>;
25+
26+
// Interactive wrapper component
27+
function PhoneInputDemo(props: ComponentProps<typeof PhoneInput>): React.JSX.Element {
28+
const [value, setValue] = useState(props.value ?? "");
29+
30+
return (
31+
<div className="space-y-4">
32+
<PhoneInput {...props} value={value} onChange={setValue} />
33+
<div className="text-muted-foreground text-sm">
34+
<strong>Value:</strong> {value || "(empty)"}
35+
</div>
36+
</div>
37+
);
38+
}
39+
40+
export const Default: Story = {
41+
render: (args: PhoneInputProps) => <PhoneInputDemo {...args} />,
42+
args: {
43+
placeholder: "Enter phone number",
44+
},
45+
};
46+
47+
export const WithValue: Story = {
48+
render: (args: PhoneInputProps) => <PhoneInputDemo {...args} />,
49+
args: {
50+
value: "+34 612 345 678",
51+
placeholder: "Enter phone number",
52+
},
53+
};
54+
55+
export const WithUSNumber: Story = {
56+
render: (args: PhoneInputProps) => <PhoneInputDemo {...args} />,
57+
args: {
58+
value: "+1 555 123 4567",
59+
placeholder: "Enter phone number",
60+
},
61+
};
62+
63+
export const Required: Story = {
64+
render: (args: PhoneInputProps) => <PhoneInputDemo {...args} />,
65+
args: {
66+
placeholder: "Enter phone number",
67+
required: true,
68+
},
69+
};
70+
71+
export const Disabled: Story = {
72+
render: (args: PhoneInputProps) => <PhoneInputDemo {...args} />,
73+
args: {
74+
value: "+44 20 7123 4567",
75+
placeholder: "Enter phone number",
76+
disabled: true,
77+
},
78+
};
79+
80+
export const WithError: Story = {
81+
render: (args: PhoneInputProps) => <PhoneInputDemo {...args} />,
82+
args: {
83+
value: "+1 555",
84+
placeholder: "Enter phone number",
85+
errorMessage: "Please enter a valid phone number",
86+
},
87+
};
88+
89+
export const RTLDirection: Story = {
90+
render: (args: PhoneInputProps) => <PhoneInputDemo {...args} />,
91+
args: {
92+
value: "+966 50 123 4567",
93+
placeholder: "أدخل رقم الهاتف",
94+
dir: "rtl",
95+
},
96+
};

0 commit comments

Comments
 (0)