Skip to content

Instantly share code, notes, and snippets.

@harveytoro
Created August 31, 2022 00:27
Show Gist options
  • Save harveytoro/4ee2abe61ea52afd43db246f84b96017 to your computer and use it in GitHub Desktop.
Save harveytoro/4ee2abe61ea52afd43db246f84b96017 to your computer and use it in GitHub Desktop.
Basic Execution: tsc technical-challenge.ts; node technical-challenge.js
// API
class Form {
public identifier: string;
public fields: Field<FieldValue>[] = [];
constructor(){
// basic identifier implementation for POC
this.identifier = Math.random().toString(16).substring(2)
}
// Adds a new field to the form
public addField(field: Field<FieldValue>): void {
this.fields.push(field);
}
// Removes a field from the form
public removeField(field: Field<FieldValue>): void {
for(let i = 0; i < this.fields.length; i++){
if(this.fields[i].identifier == field.identifier){
this.fields.splice(i, 1);
break;
}
}
}
// Validates form by calling validation on each field
public validate(): Field<FieldValue>[] {
var errorFields: Field<FieldValue>[] = [];
for(var field of this.fields){
if(!field.validateField()){
errorFields.push(field)
}
}
return errorFields;
}
// Stub submit that just validates form
public submit(): boolean {
return this.validate().length == 0;
}
}
class FieldValue {}
abstract class Field<ValueType extends FieldValue> {
public identifier: string;
public label: String;
public value: ValueType;
public isRequired: Boolean;
public defaultValue: ValueType;
public isVisible: Boolean;
protected notifyOfValueChangeFields: Field<FieldValue>[] = [];
protected conditions: {} = {}
constructor(label: string, visible: boolean = true){
this.identifier = Math.random().toString(16).substring(2)
this.label = label
this.isVisible = visible
}
// Updates the value of the field and notifies intereted fields
public updateValue(value: ValueType): void {
this.value = value;
for(var toNotify of this.notifyOfValueChangeFields){
toNotify.valueUpdated(this);
}
}
// Registeres a fields interest in being notified of value change
public notifyOnChange(field: Field<FieldValue>): void {
this.notifyOfValueChangeFields.push(field);
}
// Tells field that a conditional value has been updated
public valueUpdated(field: Field<FieldValue>): void {
if(!(field.identifier in this.conditions)){
return;
}
if(field.value == this.conditions[field.identifier]){
this.isVisible = true
}
}
// adds a conditional value to the field
public addConditional(field: Field<FieldValue>, value: FieldValue): void {
field.notifyOnChange(this);
this.conditions[field.identifier] = value;
}
// base validation of a field
public validateField(): boolean{
console.log(this.value)
if(this.isRequired && (this.value == null || this.value == undefined)){
return false
}
return true
}
}
// Plain Text box implementationf of a Field
class PlainTextField extends Field<string> {
private minSize: number | null;
private maxSize: number | null;
constructor(label: string, visible: boolean = true, minSize: number | null = null, maxSize: number | null = null){
super(label, visible)
this.minSize = minSize;
this.maxSize = maxSize;
}
public override validateField(): boolean {
if(this.minSize == null && this.maxSize == null){
return super.validateField();
}
// Ensure min and max size of field is adhered to
if((this.minSize != null && this.value.length < this.minSize) || (this.maxSize != null && this.value.length > this.maxSize)){
return false;
}
return true;
}
}
// Email field
class EmailTextField extends PlainTextField {
// stub format of POC
private format : RegExp = /.*@.*/;
public override validateField(): boolean {
return this.format.test(this.value)
}
}
// Single select field, contains an array of allowed values
// Assumes the selectable values are strings, could be numbers - wouldn't be too difficult to allow this
class SingleSelectField extends Field<string> {
public allowedValues: string[]
constructor(label: string, allowedValues: string[], visible: boolean = true){
super(label, visible)
this.allowedValues = allowedValues;
}
public override validateField(): boolean {
if(!(this.value in this.allowedValues)){
return false;
}
return true;
}
}
class BooleanField extends Field<boolean> {
constructor(label: string, defaultValue: boolean, visible: boolean = true){
super(label, visible)
this.defaultValue = defaultValue;
}
}
class FileField extends Field<{}> {
// stub, assumbe that a FileField contains an object that represents a file
// would contain file temp location, or byte data, file type etc...
}
// Use the API
const form = new Form();
const nameField = new PlainTextField("Name");
form.addField(nameField);
const emailField = new EmailTextField("Email", false);
emailField.addConditional(nameField, "John Doe")
form.addField(emailField);
console.log(emailField.isVisible); //False
nameField.updateValue("John Doe");
console.log(emailField.isVisible); //True
emailField.updateValue("jd");
console.log(emailField.validateField()); //False
emailField.updateValue("j@d.com")
console.log(emailField.validateField()); //True
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment