Skip to content

Commit cfc2c22

Browse files
authored
Merge pull request #12 from EAVFW/kba/validation
Kba/validation
2 parents a4e82dd + d2eac14 commit cfc2c22

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+1139
-630
lines changed

packages/core/src/components/error-message/ErrorMessage.module.css

Lines changed: 0 additions & 36 deletions
This file was deleted.
Lines changed: 14 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,72 +1,23 @@
1-
"use client";
2-
import React, { useState, useEffect } from 'react';
3-
import { useDelayedClickListener } from "../../hooks";
4-
import { useQuickForm } from "../../state/QuickFormContext";
5-
import { quickformtokens } from '../../style/quickformtokens';
1+
import React from 'react';
62

7-
type ErrorProps = {
8-
readonly message: string;
9-
};
3+
type ErrorMessageProps = {
4+
message: string;
5+
}
106

11-
export const ErrorMessage: React.FC<ErrorProps> = ({ message }: ErrorProps) => {
12-
const [isVisible, setIsVisible] = useState(false);
13-
const { dispatch, state } = useQuickForm();
7+
export const ErrorMessage: React.FC<ErrorMessageProps> = ({ message }) => {
8+
if (!message) return null;
149

15-
/**
16-
* DISCUSS - What cases is there for resetting error and can it be handled in reducer all alone?.
17-
* When an error is shown, upon next answer it can be cleared.
18-
* Possible a dissmis button - but i dont think it should automatically just remove when clicked.
19-
*/
20-
21-
22-
//const resetErrorMessage = () => {
23-
// if (state.errorMsg !== "") {
24-
// dispatch({ type: "SET_ERROR_MSG", msg: "" })
25-
// }
26-
//}
27-
28-
// useDelayedClickListener(resetErrorMessage);
29-
30-
useEffect(() => {
31-
if (message) {
32-
setIsVisible(true);
33-
setTimeout(() => setIsVisible(false), 350);
34-
}
35-
}, [message]);
36-
37-
if (message === "") {
38-
return <></>;
39-
}
40-
41-
const errorStyle: React.CSSProperties = {
42-
alignItems: 'flex-end',
43-
animation: isVisible ? 'slide-up 0.35s linear 1 forwards' : '',
44-
backgroundColor: 'var(--surface)',
45-
borderRadius: '3px',
46-
color: quickformtokens.onError,
47-
display: 'flex',
48-
fontSize: '1.5rem',
49-
marginTop: '15px',
50-
padding: '8px 12px',
51-
width: 'max-content',
52-
};
53-
54-
const mobileErrorStyle: React.CSSProperties = {
55-
...errorStyle,
56-
fontSize: '1.75rem',
57-
marginTop: '22px',
58-
width: '100%',
59-
};
60-
61-
const imgStyle: React.CSSProperties = {
62-
marginRight: '4px',
10+
const errorMessageStyle: React.CSSProperties = {
11+
color: 'red',
12+
textDecoration: 'underline',
13+
marginTop: '4px',
14+
fontSize: '0.875rem',
15+
fontWeight: 'normal',
6316
};
6417

6518
return (
66-
<div style={window.innerWidth <= 599 ? mobileErrorStyle : errorStyle}>
67-
{/* If there's an image you want to include inside the error message */}
68-
{/* <img src="path_to_your_image" alt="Error" style={imgStyle} /> */}
19+
<div style={errorMessageStyle}>
6920
{message}
7021
</div>
7122
);
72-
};
23+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
"use client";
2+
import React, { useState, useEffect } from 'react';
3+
import { useDelayedClickListener } from "../../hooks";
4+
import { useQuickForm } from "../../state/QuickFormContext";
5+
import { quickformtokens } from '../../style/quickformtokens';
6+
7+
type ErrorPopupProps = {
8+
readonly message: string;
9+
};
10+
11+
export const ErrorPopup: React.FC<ErrorPopupProps> = ({ message }: ErrorPopupProps) => {
12+
const [isVisible, setIsVisible] = useState(false);
13+
const { dispatch, state } = useQuickForm();
14+
15+
/**
16+
* DISCUSS - What cases is there for resetting error and can it be handled in reducer all alone?.
17+
* When an error is shown, upon next answer it can be cleared.
18+
* Possible a dissmis button - but i dont think it should automatically just remove when clicked.
19+
*/
20+
21+
22+
//const resetErrorMessage = () => {
23+
// if (state.errorMsg !== "") {
24+
// dispatch({ type: "SET_ERROR_MSG", msg: "" })
25+
// }
26+
//}
27+
28+
// useDelayedClickListener(resetErrorMessage);
29+
30+
useEffect(() => {
31+
if (message) {
32+
setIsVisible(true);
33+
setTimeout(() => setIsVisible(false), 350);
34+
}
35+
}, [message]);
36+
37+
if (message === "") {
38+
return <></>;
39+
}
40+
41+
const errorStyle: React.CSSProperties = {
42+
alignItems: 'flex-end',
43+
animation: isVisible ? 'slide-up 0.35s linear 1 forwards' : '',
44+
backgroundColor: 'var(--surface)',
45+
borderRadius: '3px',
46+
color: quickformtokens.onError,
47+
display: 'flex',
48+
fontSize: '1.5rem',
49+
marginTop: '15px',
50+
padding: '8px 12px',
51+
width: 'max-content',
52+
};
53+
54+
const mobileErrorStyle: React.CSSProperties = {
55+
...errorStyle,
56+
fontSize: '1.75rem',
57+
marginTop: '22px',
58+
width: '100%',
59+
};
60+
61+
const imgStyle: React.CSSProperties = {
62+
marginRight: '4px',
63+
};
64+
65+
return (
66+
<div style={window.innerWidth <= 599 ? mobileErrorStyle : errorStyle}>
67+
{/* If there's an image you want to include inside the error message */}
68+
{/* <img src="path_to_your_image" alt="Error" style={imgStyle} /> */}
69+
{message}
70+
</div>
71+
);
72+
};

packages/core/src/components/heading/Heading.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ type HeadingProps = {
1313
readonly label?: string;
1414
};
1515

16-
export function Heading({ children, label, style = {} }: HeadingProps) {
16+
export const Heading: React.FC<HeadingProps> = ({ children, label, style = {} }: HeadingProps) => {
1717

1818
const shouldDisplayNumber = resolveQuickFormService("headingNumberDisplayProvider")();
1919

@@ -29,7 +29,7 @@ export function Heading({ children, label, style = {} }: HeadingProps) {
2929
{shouldDisplayNumber && <span style={{ //TODO - if mobile left 0, top:-2.4rem,justifycontext start
3030
display: 'inline-flex', alignItems: 'center', gap: quickformtokens.gap1, position: "absolute", width: "100px", left: "-100px", justifyContent: "end",
3131
fontSize: quickformtokens.questionQuestionNumberFontSiez,
32-
height:"100%",
32+
height: "100%",
3333
paddingRight: quickformtokens.gap2
3434
}}>
3535

packages/core/src/components/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
export { Button } from "./button/Button";
22
export { Divider } from "./divider/Divider";
33
export { Ending } from "./ending/Ending";
4+
export { ErrorPopup } from "./error-popup/ErrorPopup";
45
export { ErrorMessage } from "./error-message/ErrorMessage";
56
export { Heading } from "./heading/Heading";
67
export { NavigationButton } from "./navigation-button/NavigationButton";

packages/core/src/components/paragraph/Paragraph.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ import { quickformtokens } from "../../style/quickformtokens";
55

66

77
type ParagraphProps = {
8-
readonly children: ReactNode;
9-
style?: React.CSSProperties;
8+
readonly children: ReactNode;
9+
style?: React.CSSProperties;
1010
};
1111

12-
export function Paragraph({ style, children }: ParagraphProps) {
12+
export const Paragraph: React.FC<ParagraphProps> = ({ style, children }: ParagraphProps) => {
1313

1414
const paragraphStyles = {
1515
fontSize: quickformtokens.questionParagraphFontSize, //'1.5rem',

packages/core/src/components/progress-bar/ProgressBar.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ type ProgressBarProps = {
55
readonly width?: number;
66
};
77

8-
export function ProgressBar({ width }: ProgressBarProps) {
8+
export const ProgressBar: React.FC<ProgressBarProps> = ({ width }: ProgressBarProps) => {
99
return (
1010
<div className={styles["progress-bar__path"]}>
1111
<div

packages/core/src/components/question/Question.tsx

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,14 @@
11
"use client";
2-
import { ReactNode } from "react";
32
import React from "react";
4-
import { Paragraph, Heading } from "..";
3+
import { Paragraph, Heading, ErrorMessage } from "..";
54
import { QuestionModel } from "../../model/QuestionModel";
65
import { useQuickForm } from "../../state/QuickFormContext";
7-
import { resolveQuickFormService } from "../../services/QuickFormServices";
8-
import { resolveInputComponent } from "../../services";
6+
import { resolveInputComponent, resolveQuickFormService } from "../../services";
97
import { quickformtokens } from "../../style/quickformtokens";
108

11-
12-
139
type QuestionProps = {
1410
model: QuestionModel;
1511
style?: React.CSSProperties;
16-
icon?: ReactNode
1712
}
1813

1914
const questionStyling: React.CSSProperties = {
@@ -35,11 +30,11 @@ export const Question: React.FC<QuestionProps> = ({ model, style }) => {
3530
const label = state.isSubmitSlide ? '' : `${state.currIdx + 1}${ql}`;
3631

3732
if (!InputType || typeof InputType === "undefined") {
38-
return <div
39-
style={{ ...questionStyling, ...style }}
40-
>
41-
Attempted to use inputtype {model.inputType} but was not able to find a matching input for question: {model.logicalName}
42-
</div>
33+
return (
34+
<div style={{ ...questionStyling, ...style }} >
35+
Unable to find a matching input for "{model.logicalName}" using input type "{model.inputType}".
36+
</div>
37+
)
4338
}
4439

4540
return (
@@ -55,12 +50,19 @@ export const Question: React.FC<QuestionProps> = ({ model, style }) => {
5550
<Paragraph >
5651
{model.paragraph}
5752
</Paragraph>
58-
59-
<InputType style={{ marginTop: quickformtokens.questionInputGap, fontSize: quickformtokens.questionInputFontSize, fontFamily: quickformtokens.fontFamily }}
60-
key={"input" + model.logicalName}
53+
<InputType
54+
key={"input-" + model.logicalName}
55+
style={
56+
{
57+
marginTop: quickformtokens.questionInputGap,
58+
fontSize: quickformtokens.questionInputFontSize,
59+
fontFamily: quickformtokens.fontFamily
60+
}
61+
}
6162
questionModel={model}
6263
{...model.inputProperties ?? {}}
6364
/>
65+
{model.validationResult?.message !== "" && <ErrorMessage message={model.validationResult?.message} />}
6466
</div>
6567
);
6668
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
"use client";
2+
import classNames from "classnames";
3+
import styles from "./BaseInput.module.css";
4+
import { ChangeEvent, useState } from "react";
5+
import { QuestionModel } from "../../../../model";
6+
import React, { CSSProperties, InputHTMLAttributes } from "react";
7+
import { useQuickForm } from "../../../../state/QuickFormContext";
8+
import { useFocusableQuestion } from "../../../../hooks/useFocusableQuestion";
9+
10+
export const BaseInputComponent = ({ questionModel, className, style, type }: { type: InputHTMLAttributes<HTMLInputElement>["type"], questionModel: QuestionModel, className?: string, style?: CSSProperties }) => {
11+
12+
const [text, setText] = useState<string>(questionModel!.output);
13+
const ref = useFocusableQuestion<HTMLInputElement>(questionModel.logicalName);
14+
const { answerQuestion } = useQuickForm();
15+
16+
const resize = () => {
17+
const input = ref.current;
18+
if (!input)
19+
return;
20+
21+
const oldvalue = input.value;
22+
23+
if (!oldvalue || oldvalue === '')
24+
input.value = input.placeholder;
25+
26+
const isOverflowed = input.scrollWidth > input.clientWidth;
27+
input.value = oldvalue;
28+
if (isOverflowed) {
29+
var style = window.getComputedStyle(input, null).getPropertyValue('font-size');
30+
input.style.fontSize = (parseFloat(style) - 1) + "px";
31+
resize();
32+
}
33+
}
34+
35+
const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
36+
if (event.target.value === "")
37+
questionModel.errorMsg = "";
38+
setText(() => event.target.value);
39+
answerQuestion(questionModel.logicalName, event.target.value, true);
40+
resize();
41+
}
42+
43+
return (
44+
<input style={style}
45+
ref={ref}
46+
type={type}
47+
className={classNames(styles.input__text, className)}
48+
placeholder={questionModel.placeholder}
49+
value={text}
50+
onChange={handleChange}
51+
/>
52+
);
53+
}

packages/core/src/components/question/input-types/email/EmailInput.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"use client";
22
import { InputComponentType, registerInputComponent } from "../../../../services/defaults/DefaultInputTypeResolver";
3-
import { BaseInputComponent } from "../text/TextInput";
3+
import { BaseInputComponent } from "../baseinput/BaseInputComponent";
44
import { emailInputSchema } from "./EmailInputSchema";
55

66
export const EmailInput: InputComponentType = (props) => {
Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,4 @@
1-
// export * from './BankAccountInput';
2-
// export * from './CPRInput';
3-
// export * from './EmailInput';
4-
// export * from '../ending/Ending';
5-
// export * from './FirstNameInput';
6-
// export * from './IndustryInput';
7-
// export * from '../intro/Intro';
8-
// export * from './LastNameInput';
9-
// export * from './PhoneInput';
10-
// export * from './RoleInput';
11-
//export * from './dropdown/DropDownInput';
12-
export * from './text/TextInput';
13-
export * from './multiline/MultilineInput';
14-
15-
161
import "./email/EmailInput";
17-
import "./phone/PhoneInput";
2+
import "./multiline/MultilineInput";
3+
import "./phone/PhoneInput";
4+
import "./text/TextInput";

0 commit comments

Comments
 (0)