Skip to content
Open
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
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions src/card-section/card-section.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { DecoratedTextWidget } from '../widgets/decorated-text/decorated-text.wi
import { DividerWidget } from '../widgets/divider/divider.widget';
import { SelectionInputWidget } from '../widgets/selection-input/selection-input.widget';
import { TextParagraphWidget } from '../widgets/text-paragraph/text-paragraph.widget';
import { TextInputWidget } from '../widgets/text-input/text-input.widget';

export type CardSectionProps = {
header?: string;
Expand All @@ -18,6 +19,7 @@ export type CardSectionProps = {
const widgetNameMap = new Map([
[SelectionInputWidget.name, 'selectionInput'],
[TextParagraphWidget.name, 'textParagraph'],
[TextInputWidget.name, 'textInput'],
[DecoratedTextWidget.name, 'decoratedText'],
[ButtonListWidget.name, 'buttonList'],
[DateTimePickerWidget.name, 'dateTimePicker'],
Expand Down
5 changes: 5 additions & 0 deletions src/card-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { DecoratedTextBuilder } from './widgets/decorated-text/decorated-text.bu
import { DividerBuilder } from './widgets/divider/divider.builder';
import { SelectionInputBuilder } from './widgets/selection-input/selection-input.builder';
import { TextParagraphBuilder } from './widgets/text-paragraph/text-paragraph.builder';
import { TextInputBuilder } from './widgets/text-input/text-input.builder';
import { Color } from './shared/color';
import { HeaderBuilder } from './header/header.builder';

Expand Down Expand Up @@ -95,6 +96,10 @@ export class CardService {
public static newDivider(): DividerBuilder {
return new DividerBuilder();
}

public static newTextInput(): TextInputBuilder {
return new TextInputBuilder();
}
}

export namespace CardService {
Expand Down
3 changes: 3 additions & 0 deletions src/shared/suggestions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export type Suggestions = {
items: string[];
};
4 changes: 4 additions & 0 deletions src/shared/validation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export type Validation = {
characterLimit?: number;
inputType?: string;
};
4 changes: 4 additions & 0 deletions src/widgets/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,21 @@ import { SelectionInputBuilder } from './selection-input/selection-input.builder
import { SelectionInputWidget } from './selection-input/selection-input.widget';
import { TextParagraphBuilder } from './text-paragraph/text-paragraph.builder';
import { TextParagraphWidget } from './text-paragraph/text-paragraph.widget';
import { TextInputBuilder } from './text-input/text-input.builder';
import { TextInputWidget } from './text-input/text-input.widget';

export type Widget =
| SelectionInputWidget
| TextParagraphWidget
| TextInputWidget
| ButtonListWidget
| DateTimePickerWidget
| DividerWidget;

export type WidgetBuilder =
| SelectionInputBuilder
| TextParagraphBuilder
| TextInputBuilder
| ButtonListBuilder
| DateTimePickerBuilder
| DividerBuilder;
74 changes: 74 additions & 0 deletions src/widgets/text-input/text-input.builder.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { ActionBuilder } from '../../actions/action.builder';
import { TextInputBuilder } from './text-input.builder';

describe('TextInputBuilder', () => {
let sut: TextInputBuilder;

beforeEach(() => {
sut = new TextInputBuilder();
});

test('throws error if name is not set', () => {
sut.setTitle('Test Label');
expect(() => sut.build()).toThrow('TextInput name is required');
});

test('throws error if label is not set', () => {
sut.setFieldName('testField');
expect(() => sut.build()).toThrow('TextInput label is required');
});

test('builds with required fields', () => {
sut.setFieldName('testField').setTitle('Test Label');
const output = sut.build();
expect(output.name).toBe('testField');
expect(output.label).toBe('Test Label');
});

test('sets hintText', () => {
sut.setFieldName('testField').setTitle('Test Label').setHint('Test hint');
const output = sut.build();
expect(output.hintText).toBe('Test hint');
});

test('sets value', () => {
sut.setFieldName('testField').setTitle('Test Label').setValue('Test value');
const output = sut.build();
expect(output.value).toBe('Test value');
});

test('sets type to MULTIPLE_LINE when multiline is true', () => {
sut.setFieldName('testField').setTitle('Test Label').setMultiline(true);
const output = sut.build();
expect(output.type).toBe('MULTIPLE_LINE');
});

test('sets type to SINGLE_LINE when multiline is false', () => {
sut.setFieldName('testField').setTitle('Test Label').setMultiline(false);
const output = sut.build();
expect(output.type).toBe('SINGLE_LINE');
});

test('sets onChange action', () => {
const action = new ActionBuilder().setFunctionName('testFunction');
sut.setFieldName('testField').setTitle('Test Label').setOnChangeAction(action);
const output = sut.build();
expect(output.onChangeAction).toBeDefined();
expect(output.onChangeAction?.function).toBe('testFunction');
});

test('sets initialSuggestions', () => {
const suggestions = { items: ['item1', 'item2'] };
sut.setFieldName('testField').setTitle('Test Label').setSuggestions(suggestions);
const output = sut.build();
expect(output.initialSuggestions).toBe(suggestions);
});

test('sets validation', () => {
const validation = { characterLimit: 100, inputType: 'email' };
sut.setFieldName('testField').setTitle('Test Label').setValidation(validation);
const output = sut.build();
expect(output.validation).toBe(validation);
});

});
88 changes: 88 additions & 0 deletions src/widgets/text-input/text-input.builder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { ActionBuilder } from '../../actions/action.builder';
import { Suggestions } from '../../shared/suggestions';
import { Validation } from '../../shared/validation';
import {
TextInputWidget,
TextInputWidgetProps,
} from './text-input.widget';

type TextInputBuilderProps = {
name?: string;
label?: string;
hintText?: string;
value?: string;
multiline?: boolean;
onChangeActionBuilder?: ActionBuilder;
initialSuggestions?: Suggestions;
validation?: Validation;
};

export class TextInputBuilder {
private _props: TextInputBuilderProps = {};

public build(): TextInputWidget {
if (!this._props.name) {
throw new Error('TextInput name is required');
}
if (!this._props.label) {
throw new Error('TextInput label is required');
}

const hasAction = !!this._props.onChangeActionBuilder;
const props: TextInputWidgetProps = {
name: this._props.name,
label: this._props.label,
hintText: this._props.hintText,
value: this._props.value,
type: this._props.multiline ? 'MULTIPLE_LINE' : 'SINGLE_LINE',
initialSuggestions: this._props.initialSuggestions,
validation: this._props.validation,
...(hasAction && {
onChangeAction: this._props.onChangeActionBuilder!.build(),
}),
};

return new TextInputWidget(props);
}

public setFieldName(fieldName: string): this {
this._props.name = fieldName;
return this;
}

public setTitle(title: string): this {
this._props.label = title;
return this;
}

public setHint(hint: string): this {
this._props.hintText = hint;
return this;
}

public setValue(value: string): this {
this._props.value = value;
return this;
}

public setMultiline(multiline: boolean): this {
this._props.multiline = multiline;
return this;
}

public setOnChangeAction(action: ActionBuilder): this {
this._props.onChangeActionBuilder = action;
return this;
}

public setSuggestions(suggestions: Suggestions): this {
this._props.initialSuggestions = suggestions;
return this;
}


public setValidation(validation: Validation): this {
this._props.validation = validation;
return this;
}
}
39 changes: 39 additions & 0 deletions src/widgets/text-input/text-input.widget.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { Action } from '../../actions/action';
import { Suggestions } from '../../shared/suggestions';
import { Validation } from '../../shared/validation';

export type TextInputWidgetProps = {
name: string;
label: string;
hintText?: string;
value?: string;
type?: 'SINGLE_LINE' | 'MULTIPLE_LINE';
onChangeAction?: Action;
initialSuggestions?: Suggestions;
validation?: Validation;
placeholderText?: string;
};

export class TextInputWidget {
public readonly name: string;
public readonly label: string;
public readonly hintText?: string;
public readonly value?: string;
public readonly type?: 'SINGLE_LINE' | 'MULTIPLE_LINE';
public readonly onChangeAction?: Action;
public readonly initialSuggestions?: Suggestions;
public readonly validation?: Validation;
public readonly placeholderText?: string;

public constructor(props: TextInputWidgetProps) {
this.name = props.name;
this.label = props.label;
this.hintText = props.hintText;
this.value = props.value;
this.type = props.type;
this.onChangeAction = props.onChangeAction;
this.initialSuggestions = props.initialSuggestions;
this.validation = props.validation;
this.placeholderText = props.placeholderText;
}
}