Skip to content

Commit 3aff78b

Browse files
committed
fix: removed import of inputcontrols in QuickForm.tsx and adjust playground to use them from components folder instead
1 parent 4c68d3d commit 3aff78b

File tree

15 files changed

+618
-9
lines changed

15 files changed

+618
-9
lines changed

packages/core/src/components/QuickForm.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
import React from 'react';
33
import { useQuickForm } from "../state/QuickFormContext";
44
import { Ending, Submit, Intro, SlideRenderer } from "./index";
5-
import "./question/input-types/index";
65

76
export const QuickForm: React.FC = () => {
87
const { state, setIntroVisited } = useQuickForm();

packages/playground/Index.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
import ReactDOM from 'react-dom';
21
import { App } from './src/App';
2+
import { createRoot } from 'react-dom/client';
33

44
const container = document.getElementById('root');
5-
6-
ReactDOM.render(<App />, container);
5+
createRoot(container).render(<App />);

packages/playground/src/App.tsx

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,8 @@ import { QuickFormDefinition } from '../../core/src/model';
33
import testdata from "./data/allInputControlsMultipleSlidesTest.json";
44
import { QuickFormProvider } from '../../core/src/state';
55
import { NavigationButton, QuickForm } from '../../core/src/components';
6-
import "./components/buttons-input/ButtonsInput";
7-
import "./components/checkbox-input/CheckboxInput";
8-
import "./components/radio-input/RadioInput";
9-
import "./components/slider-input/SliderInput";
10-
import { QuickFormContainer } from './components/container/QuickFormContainer';
6+
import { QuickFormContainer } from './components';
7+
import "./components/index";
118

129
const containerStyling: React.CSSProperties = {
1310
width: '100%',
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
import { quickformtokens, useHandleEnterKeypress, useQuickForm } from "@eavfw/quickform-core";
2+
import { useFocusableQuestion } from "@eavfw/quickform-core/src/hooks/useFocusableQuestion";
3+
import { CSSProperties, ChangeEvent, InputHTMLAttributes, useEffect, useState } from "react";
4+
import { makeStyles, mergeClasses, shorthands } from '@griffel/react';
5+
import { QuestionModel } from "@eavfw/quickform-core/src/model";
6+
import { IconResolver, IconType } from "../../../../core/src/components/icons/IconResolver";
7+
8+
import { trace } from "@opentelemetry/api";
9+
10+
const tracer = trace.getTracer("quickform", "1.0.0");
11+
12+
const useInputTextStyles = makeStyles({
13+
inputContainer: {
14+
display: 'flex',
15+
alignItems: 'center',
16+
position: 'relative',
17+
backgroundColor: 'transparent',
18+
color: 'var(--on-surface)',
19+
width: '100%',
20+
...shorthands.borderTop('none'),
21+
...shorthands.borderLeft('none'),
22+
...shorthands.borderRight('none'),
23+
...shorthands.borderBottom("1px", "solid", `${quickformtokens?.questionPlaceholderColor || "black"}`),
24+
25+
':focus': {
26+
...shorthands.borderBottom("1px", "solid", `${quickformtokens?.primary || "blue"}`),
27+
paddingBottom: '8px',
28+
},
29+
},
30+
inputText: {
31+
color: 'var(--on-surface)',
32+
backgroundColor: 'transparent',
33+
fontSize: quickformtokens?.questionInputFontSize || "16px",
34+
marginTop: '8px',
35+
paddingBottom: '9px',
36+
width: '100%',
37+
...shorthands.border('none'),
38+
39+
'@media screen and (max-width: 599px)': {
40+
fontSize: quickformtokens?.questionInputFontSize || "16px",
41+
marginTop: '32px',
42+
},
43+
44+
},
45+
46+
inputIcon: {
47+
marginTop: '8px',
48+
paddingBottom: '9px',
49+
},
50+
iconLeft: {
51+
left: '0',
52+
paddingRight: '15px'
53+
},
54+
iconRight: {
55+
right: '0',
56+
paddingLeft: '15px'
57+
},
58+
});
59+
60+
type BaseInputComponentProps = {
61+
type: InputHTMLAttributes<HTMLInputElement>["type"],
62+
questionModel: QuestionModel,
63+
beforeIcon?: IconType;
64+
afterIcon?: IconType
65+
style?: CSSProperties
66+
className?: string,
67+
}
68+
69+
70+
71+
export const BaseInputComponent: React.FC<BaseInputComponentProps> = ({ questionModel, className, style, type, beforeIcon, afterIcon }) => {
72+
73+
const [text, setText] = useState<string>(questionModel!.output);
74+
const ref = useFocusableQuestion<HTMLInputElement>(questionModel.logicalName);
75+
const { answerQuestion } = useQuickForm();
76+
const styles = useInputTextStyles();
77+
78+
const span = trace.getActiveSpan();
79+
80+
if (span) {
81+
span.addEvent("BaseInputComponent:render");
82+
}
83+
84+
85+
86+
const resize = () => {
87+
const input = ref.current;
88+
if (!input)
89+
return;
90+
91+
const oldvalue = input.value;
92+
93+
if (!oldvalue || oldvalue === '')
94+
input.value = input.placeholder;
95+
96+
const isOverflowed = input.scrollWidth > input.clientWidth;
97+
input.value = oldvalue;
98+
if (isOverflowed) {
99+
var style = window.getComputedStyle(input, null).getPropertyValue('font-size');
100+
input.style.fontSize = (parseFloat(style) - 1) + "px";
101+
resize();
102+
}
103+
}
104+
105+
const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
106+
console.log("BaseInputComponent:handleChange", event.target.value);
107+
if (span) {
108+
span.addEvent("BaseInputComponent:handleChange", { 'value': event.target.value });
109+
}
110+
//EXPLAIN: WHY IS THIS HERE? If no reason, lets remove.
111+
if (event.target.value === "")
112+
questionModel.errorMsg = "";
113+
114+
115+
setText(() => event.target.value);
116+
answerQuestion(questionModel.logicalName, event.target.value, true);
117+
resize();
118+
}
119+
120+
/**
121+
* The input control is responsible of setting itself focused when becoming active.
122+
* - We should also listen to inputcontrols being focused and if not active, trigger a reducer that sets it to active. Ultimately removing active from other questions.
123+
* This happens right now when an answer is given (intermediate or not), so not critical.
124+
*/
125+
useEffect(() => {
126+
if (questionModel.isActive)
127+
ref.current?.focus();
128+
}, [questionModel.isActive]);
129+
130+
/**
131+
* While a base input component is active we should answer the question upon enter.
132+
*/
133+
useHandleEnterKeypress("baseinput", !questionModel.isActive, () => {
134+
answerQuestion(questionModel.logicalName, text, false);
135+
});
136+
137+
return (
138+
<div className={mergeClasses(styles.inputContainer, className)} style={style}>
139+
{beforeIcon &&
140+
<IconResolver
141+
type={beforeIcon}
142+
className={mergeClasses(styles.inputIcon, styles.iconLeft)}
143+
size={18}
144+
color={quickformtokens.primary}
145+
/>
146+
}
147+
<input
148+
style={{ outline: 'none', }}
149+
ref={ref}
150+
type={type}
151+
className={styles.inputText}
152+
placeholder={questionModel.placeholder}
153+
value={text}
154+
onChange={handleChange}
155+
/>
156+
{afterIcon &&
157+
<IconResolver
158+
type={afterIcon}
159+
className={mergeClasses(styles.inputIcon, styles.iconRight)}
160+
size={18}
161+
color={quickformtokens.primary}
162+
/>
163+
}
164+
</div>
165+
);
166+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { InputComponentType, registerInputComponent } from "@eavfw/quickform-core";
2+
import { BaseInputComponent } from "../baseinput/BaseInputComponent";
3+
import { emailInputSchema } from "./EmailInputSchema";
4+
import { IconType } from "../../../../core/src/components/icons/IconResolver";
5+
6+
export type EmailProperties = {
7+
inputType: "email";
8+
defaultValue?: string;
9+
beforeIcon?: IconType;
10+
afterIcon?: IconType
11+
}
12+
13+
export const EmailInput: InputComponentType<EmailProperties> = (props) => {
14+
return <BaseInputComponent type="email" {...props} />
15+
}
16+
17+
/* This property assignment grants QuickformDesigner metadata information about which properties the inputcomponent needs */
18+
EmailInput.inputSchema = emailInputSchema;
19+
registerInputComponent("email", EmailInput);
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import { InputComponentMetadata } from "@eavfw/quickform-core";
2+
import { InputPropertiesTypes } from "@eavfw/quickform-core/src/model";
3+
4+
export const emailInputSchema: InputComponentMetadata<InputPropertiesTypes> = {
5+
label: "Email",
6+
uiSchema: {
7+
paragraph: {
8+
"ui:widget": "textarea"
9+
}
10+
},
11+
schema: {
12+
type: "object",
13+
14+
properties: {
15+
text: {
16+
title: "Text",
17+
type: "string"
18+
},
19+
placeholder: {
20+
title: "Placeholder",
21+
type: "string"
22+
},
23+
paragraph: {
24+
title: "Paragraph",
25+
type: "string"
26+
},
27+
beforeIcon: {
28+
enum: [
29+
"Phone",
30+
"Email",
31+
"User"
32+
],
33+
// @ts-ignore
34+
"enumNames": [
35+
"Phone",
36+
"Email",
37+
"User"
38+
]
39+
},
40+
afterIcon: {
41+
enum: [
42+
"Phone",
43+
"Email",
44+
"User"
45+
],
46+
// @ts-ignore
47+
"enumNames": [
48+
"Phone",
49+
"Email",
50+
"User"
51+
]
52+
}
53+
}
54+
},
55+
field: {
56+
type: "text",
57+
}
58+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { ButtonsInput } from './buttons-input/ButtonsInput';
2+
import { CheckboxInput } from './checkbox-input/CheckboxInput';
3+
import { EmailInput } from './email/EmailInput';
4+
import { MultilineInput } from './multiline/MultilineInput';
5+
import { OverviewList } from './overview-list/OverviewList';
6+
import { PhoneInput } from './phone/PhoneInput';
7+
import { QuickFormContainer } from './container/QuickFormContainer';
8+
import { RadioInput } from './radio-input/RadioInput';
9+
import { SliderInput } from './slider-input/SliderInput';
10+
import { TextInput } from './text/TextInput';
11+
import { ToggleOverviewButton } from './toggle-overview-button/ToggleOverviewButton';
12+
13+
export {
14+
ButtonsInput,
15+
CheckboxInput,
16+
EmailInput,
17+
MultilineInput,
18+
OverviewList,
19+
PhoneInput,
20+
QuickFormContainer,
21+
RadioInput,
22+
SliderInput,
23+
TextInput,
24+
ToggleOverviewButton
25+
};
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
"use client";
2+
import React, { ChangeEvent, useEffect, useRef, useState } from "react";
3+
import { makeStyles, shorthands } from "@griffel/react";
4+
import { useQuickForm, InputComponentType, registerInputComponent, quickformtokens } from "@eavfw/quickform-core";
5+
import { multilineInputSchema } from "./MultilineInputSchema";
6+
import { MultilineProperties } from "../../../../core/src/model";
7+
8+
const useInputTextStyles = makeStyles({
9+
inputText: {
10+
backgroundColor: 'transparent',
11+
...shorthands.border(`1px solid ${quickformtokens.onSurface}`),
12+
...shorthands.borderRadius('5px'),
13+
color: quickformtokens.onSurface,
14+
fontSize: quickformtokens.multilineTextFontSize,
15+
marginTop: '15px',
16+
paddingBottom: '9px',
17+
width: '100%',
18+
maxHeight: '8rem', // Set maximum height for three lines
19+
height: '8rem',
20+
resize: 'vertical',
21+
...shorthands.overflow('auto'),
22+
'&:focus-visible': {
23+
...shorthands.borderBottom("2px", "solid", `${quickformtokens.onSurface}`),
24+
...shorthands.outline('none'),
25+
paddingBottom: '8px'
26+
},
27+
'&::placeholder': {
28+
color: quickformtokens.onSurface,
29+
opacity: quickformtokens.mediumEmphasisOpacity,
30+
},
31+
'&::-ms-input-placeholder': {
32+
color: quickformtokens.onSurface,
33+
opacity: quickformtokens.mediumEmphasisOpacity,
34+
},
35+
'@media screen and (max-width: 599px)': {
36+
fontSize: quickformtokens.multilineTextMobileFontSize,
37+
marginTop: '32px',
38+
},
39+
},
40+
});
41+
42+
export const MultilineInput: InputComponentType<MultilineProperties> = ({ questionModel }) => {
43+
const styles = useInputTextStyles();
44+
const { isFirstQuestionInCurrentSlide, answerQuestion, state } = useQuickForm();
45+
const { placeholder, output } = questionModel;
46+
const [text, setText] = useState<string>(output || '');
47+
const ref = useRef<HTMLTextAreaElement>(null);
48+
49+
const handleChange = (event: ChangeEvent<HTMLTextAreaElement>) => {
50+
const newValue = event.target.value.replace(/\r?\n/g, '\n'); // Normalize newline characters
51+
setText(newValue);
52+
answerQuestion(questionModel.logicalName, newValue, true);
53+
};
54+
55+
/**
56+
* The input control is responsible of setting itself focused when becoming active.
57+
* - We should also listen to inputcontrols being focused and if not active, trigger a reducer that sets it to active. Ultimately removing active from other questions.
58+
* This happens right now when an answer is given (intermediate or not), so not critical.
59+
*/
60+
useEffect(() => {
61+
if (questionModel.isActive || ref.current && isFirstQuestionInCurrentSlide(questionModel.logicalName))
62+
ref.current?.focus();
63+
}, [ref, isFirstQuestionInCurrentSlide, questionModel.logicalName, questionModel.isActive]);
64+
65+
return (
66+
<textarea onBlur={() => answerQuestion(questionModel.logicalName, text, false)}
67+
ref={ref}
68+
className={styles.inputText}
69+
placeholder={placeholder}
70+
value={text}
71+
onChange={handleChange}
72+
style={{ width: '100%' }}
73+
/>
74+
);
75+
};
76+
77+
MultilineInput.inputSchema = multilineInputSchema;
78+
registerInputComponent("multilinetext", MultilineInput);

0 commit comments

Comments
 (0)