Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(application-system): new shared component buildFieldsRepeaterField #16871

Merged
merged 17 commits into from
Nov 26, 2024
Merged
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
43 changes: 43 additions & 0 deletions libs/application/core/src/lib/fieldBuilders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import {
SliderField,
MaybeWithApplication,
MaybeWithApplicationAndFieldAndLocale,
FieldsRepeaterField,
} from '@island.is/application/types'
import { Locale } from '@island.is/shared/types'
import { Colors } from '@island.is/island-ui/theme'
Expand Down Expand Up @@ -867,6 +868,48 @@ export const buildTableRepeaterField = (
}
}

export const buildFieldsRepeaterField = (
data: Omit<FieldsRepeaterField, 'type' | 'component' | 'children'>,
): FieldsRepeaterField => {
const {
fields,
table,
title,
titleVariant,
formTitle,
formTitleVariant,
formTitleNumbering,
marginTop,
marginBottom,
removeItemButtonText,
addItemButtonText,
saveItemButtonText,
minRows,
maxRows,
} = data

return {
...extractCommonFields(data),
children: undefined,
type: FieldTypes.FIELDS_REPEATER,
component: FieldComponents.FIELDS_REPEATER,
fields,
table,
title,
titleVariant,
formTitle,
formTitleVariant,
formTitleNumbering,
marginTop,
marginBottom,
removeItemButtonText,
addItemButtonText,
saveItemButtonText,
minRows,
maxRows,
}
}

export const buildStaticTableField = (
data: Omit<
StaticTableField,
Expand Down
5 changes: 5 additions & 0 deletions libs/application/core/src/lib/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ export const coreMessages = defineMessages({
defaultMessage: 'Bæta við',
description: 'Add button',
},
buttonRemove: {
id: 'application.system:button.remove',
defaultMessage: 'Fjarlægja',
description: 'Remove button',
},
buttonCancel: {
id: 'application.system:button.cancel',
defaultMessage: 'Hætta við',
Expand Down
46 changes: 42 additions & 4 deletions libs/application/types/src/lib/Fields.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export type TagVariant =
| 'mint'
| 'disabled'

export type TableRepeaterFields =
export type RepeaterFields =
| 'input'
| 'select'
| 'radio'
Expand All @@ -82,8 +82,8 @@ type TableRepeaterOptions =
activeField?: Record<string, string>,
) => RepeaterOption[] | [])

export type TableRepeaterItem = {
component: TableRepeaterFields
export type RepeaterItem = {
component: RepeaterFields
jonnigs marked this conversation as resolved.
Show resolved Hide resolved
/**
* Defaults to true
*/
Expand Down Expand Up @@ -253,6 +253,7 @@ export enum FieldTypes {
NATIONAL_ID_WITH_NAME = 'NATIONAL_ID_WITH_NAME',
ACTION_CARD_LIST = 'ACTION_CARD_LIST',
TABLE_REPEATER = 'TABLE_REPEATER',
FIELDS_REPEATER = 'FIELDS_REPEATER',
jonnigs marked this conversation as resolved.
Show resolved Hide resolved
HIDDEN_INPUT = 'HIDDEN_INPUT',
HIDDEN_INPUT_WITH_WATCHED_VALUE = 'HIDDEN_INPUT_WITH_WATCHED_VALUE',
FIND_VEHICLE = 'FIND_VEHICLE',
Expand Down Expand Up @@ -289,6 +290,7 @@ export enum FieldComponents {
NATIONAL_ID_WITH_NAME = 'NationalIdWithNameFormField',
ACTION_CARD_LIST = 'ActionCardListFormField',
TABLE_REPEATER = 'TableRepeaterFormField',
FIELDS_REPEATER = 'FieldsRepeaterFormField',
HIDDEN_INPUT = 'HiddenInputFormField',
FIND_VEHICLE = 'FindVehicleFormField',
VEHICLE_RADIO = 'VehicleRadioFormField',
Expand Down Expand Up @@ -639,7 +641,7 @@ export type TableRepeaterField = BaseField & {
marginTop?: ResponsiveProp<Space>
marginBottom?: ResponsiveProp<Space>
titleVariant?: TitleVariants
fields: Record<string, TableRepeaterItem>
fields: Record<string, RepeaterItem>
/**
* Maximum rows that can be added to the table.
* When the maximum is reached, the button to add a new row is disabled.
Expand All @@ -659,6 +661,41 @@ export type TableRepeaterField = BaseField & {
format?: Record<string, (value: string) => string | StaticText>
}
}

export type FieldsRepeaterField = BaseField & {
readonly type: FieldTypes.FIELDS_REPEATER
component: FieldComponents.FIELDS_REPEATER
titleVariant?: TitleVariants
formTitle?: StaticText
formTitleVariant?: TitleVariants
formTitleNumbering?: 'prefix' | 'suffix' | 'none'
removeItemButtonText?: StaticText
addItemButtonText?: StaticText
saveItemButtonText?: StaticText
marginTop?: ResponsiveProp<Space>
marginBottom?: ResponsiveProp<Space>
fields: Record<string, RepeaterItem>
/**
* Maximum rows that can be added to the table.
* When the maximum is reached, the button to add a new row is disabled.
*/
minRows?: number
maxRows?: number
table?: {
/**
* List of strings to render,
* if not provided it will be auto generated from the fields
*/
header?: StaticText[]
/**
* List of field id's to render,
* if not provided it will be auto generated from the fields
*/
rows?: string[]
format?: Record<string, (value: string) => string | StaticText>
}
}

export interface FindVehicleField extends InputField {
readonly type: FieldTypes.FIND_VEHICLE
component: FieldComponents.FIND_VEHICLE
Expand Down Expand Up @@ -786,6 +823,7 @@ export type Field =
| NationalIdWithNameField
| ActionCardListField
| TableRepeaterField
| FieldsRepeaterField
| HiddenInputWithWatchedValueField
| HiddenInputField
| FindVehicleField
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import {
Meta,
Story,
Canvas,
ArgsTable,
Description,
Source,
} from '@storybook/addon-docs'
import { dedent } from 'ts-dedent'

import { FieldsRepeaterFormField } from './FieldsRepeaterFormField'

export const createMockApplication = (data = {}) => ({
id: '123',
assignees: [],
state: data.state || 'draft',
applicant: '111111-3000',
typeId: data.typeId || 'ExampleForm',
modified: new Date(),
created: new Date(),
attachments: {},
answers: data.answers || {},
externalData: data.externalData || {},
})

<Meta
title="Application System/FieldsRepeaterFormField"
component={FieldsRepeaterFormField}
/>

# TableRepeaterFormField

### Usage in a template

You can create a FieldsRepeaterFormField using the following function `FieldsTableRepeaterField`.
Validation should be done via zod schema.

<Source
language="typescript"
code={dedent(`
buildFieldsRepeaterField({
id: 'fieldsRepeater',
title: 'My fields repeater',
description: 'Description for the fields repeater',
formTitle: 'Title for each form',
minRows: 2,
maxRows: 4,
fields: {
email: {
component: 'input',
label: 'Email',
type: 'email',
},
phoneNumber: {
component: 'input',
label: 'Phonenumber',
type: 'tel',
format: '###-####',
placeholder: '000-0000',
},
radio: {
component: 'select',
label: 'Radio',
placeholder: 'placeholder',
options: [{ label: 'Option 1', value: '1' }, { label: 'Option 2', value: '2' }],
},
},
})`)}
/>

The previous configuration object will result in the following component:

<Canvas>
<Story name="Default">
<FieldsRepeaterFormField
application={createMockApplication()}
field={{
id: 'field.id',
title: 'My repeater',
formTitle: 'Add new contact',
addItemButtonText: 'Add new contact',
saveItemButtonText: 'Save',
fields: {
email: {
component: 'input',
type: 'email',
label: 'Email',
},
phone: {
component: 'input',
label: 'Phone',
type: 'tel',
format: '###-####',
placeholder: '000-0000',
},
email: {
component: 'input',
label: 'Email',
type: 'email',
width: 'half',
},
radio: {
component: 'radio',
label: 'Radio',
options: [
{ label: 'Option 1', value: '1' },
{ label: 'Option 2', value: '2' },
],
placeholder: 'placeholder',
},
},
table: {
format: {
phone: (value) => value.replace(/^(.{3})/, '$1-'),
},
},
}}
/>
</Story>
</Canvas>

You can also use this field into a custom component by using `<FieldsRepeaterFormField field={...} />` with the configuration object defined above.

# Props

## FieldsRepeaterFormField

<ArgsTable of={FieldsRepeaterFormField} />
Loading