-
-
Notifications
You must be signed in to change notification settings - Fork 466
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor(console): extract the KeyValueInput ds component
extract the KeyValueInput ds component
- Loading branch information
Showing
4 changed files
with
213 additions
and
89 deletions.
There are no files selected for viewing
25 changes: 25 additions & 0 deletions
25
packages/console/src/ds-components/KeyValueInputField/index.module.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
@use '@/scss/underscore' as _; | ||
|
||
.field { | ||
margin-bottom: _.unit(3); | ||
|
||
.input { | ||
display: flex; | ||
gap: _.unit(2); | ||
align-items: center; | ||
|
||
.keyInput { | ||
flex: 1; | ||
} | ||
|
||
.valueInput { | ||
flex: 2; | ||
} | ||
} | ||
|
||
.error { | ||
font: var(--font-body-2); | ||
color: var(--color-error); | ||
margin-top: _.unit(1); | ||
} | ||
} |
127 changes: 127 additions & 0 deletions
127
packages/console/src/ds-components/KeyValueInputField/index.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
import { type AdminConsoleKey } from '@logto/phrases'; | ||
import { type ReactElement, useCallback } from 'react'; | ||
import { type FieldError } from 'react-hook-form'; | ||
|
||
import CirclePlus from '@/assets/icons/circle-plus.svg'; | ||
import Minus from '@/assets/icons/minus.svg'; | ||
import Button from '@/ds-components/Button'; | ||
import type DangerousRaw from '@/ds-components/DangerousRaw'; | ||
import FormField from '@/ds-components/FormField'; | ||
import IconButton from '@/ds-components/IconButton'; | ||
import TextInput, { type Props as TextInputProps } from '@/ds-components/TextInput'; | ||
|
||
import * as styles from './index.module.scss'; | ||
|
||
type FieldType = { | ||
key: string; | ||
value: string; | ||
}; | ||
|
||
type ErrorType = { | ||
[K in keyof FieldType]?: FieldError | string | undefined; | ||
}; | ||
|
||
// TextInput props getter | ||
type InputFieldPropsGetter = { | ||
[K in keyof FieldType]: (index: number) => Omit<TextInputProps, 'ref'>; | ||
}; | ||
|
||
type Props = { | ||
title: AdminConsoleKey | ReactElement<typeof DangerousRaw>; | ||
tip?: string; | ||
fields: Array<FieldType & { id: string }>; // Id is required to uniquely identify each field | ||
errors?: Array<ErrorType | undefined>; | ||
getInputFieldProps: InputFieldPropsGetter; | ||
onRemove: (index: number) => void; | ||
onAppend: (field: FieldType) => void; | ||
}; | ||
|
||
/** | ||
* UI component for key-value input field. | ||
* | ||
* This component is used to add multiple key-value pairs. | ||
* For most of the cases, it is designed to be used along with react-hook-form. | ||
* All the input properties are registered with react-hook-form. | ||
* | ||
* @param {string} title - The title of the field. | ||
* @param {string} [tip] - The tip for the field. | ||
* @param {FieldType} fields - The array of key-value pairs. @see {@link https://react-hook-form.com/docs/usefieldarray} | ||
* @param {ErrorType[]} [errors] - The array of errors for each field. Accepts both string and FieldError from RHF. | ||
* @param {Function} onRemove - The function to remove a field. @see {@link https://react-hook-form.com/docs/usefieldarray} | ||
* @param {Function} onAppend - The function to append a new field. @see {@link https://react-hook-form.com/docs/usefieldarray} | ||
* @param {InputFieldPropsGetter} getInputFieldProps - The function bundle to get the input field props for each field. e.g. Use React Hook Form's register method to register the input field. | ||
*/ | ||
function KeyValueInputField({ | ||
title, | ||
tip, | ||
fields, | ||
errors, | ||
getInputFieldProps, | ||
onRemove, | ||
onAppend, | ||
}: Props) { | ||
const renderErrors = useCallback( | ||
(index: number, key: keyof FieldType) => { | ||
const error = errors?.[index]?.[key]; | ||
|
||
if (!error) { | ||
return null; | ||
} | ||
|
||
if (typeof error === 'string') { | ||
return <div className={styles.error}>{error}</div>; | ||
} | ||
|
||
return <div className={styles.error}>{error.message}</div>; | ||
}, | ||
[errors] | ||
); | ||
|
||
return ( | ||
<FormField title={title} tip={tip}> | ||
{fields.map((field, index) => { | ||
return ( | ||
// Use id as the element key if it exists (generated by react-hook-form useFieldArray method), otherwise use the key | ||
<div key={field.id} className={styles.field}> | ||
<div className={styles.input}> | ||
<TextInput | ||
className={styles.keyInput} | ||
placeholder="Key" | ||
error={Boolean(errors?.[index]?.key)} | ||
{...getInputFieldProps.key(index)} | ||
/> | ||
<TextInput | ||
className={styles.valueInput} | ||
placeholder="Value" | ||
error={Boolean(errors?.[index]?.value)} | ||
{...getInputFieldProps.value(index)} | ||
/> | ||
{fields.length > 1 && ( | ||
<IconButton | ||
onClick={() => { | ||
onRemove(index); | ||
}} | ||
> | ||
<Minus /> | ||
</IconButton> | ||
)} | ||
</div> | ||
{renderErrors(index, 'key')} | ||
{renderErrors(index, 'value')} | ||
</div> | ||
); | ||
})} | ||
<Button | ||
size="small" | ||
type="text" | ||
title="general.add_another" | ||
icon={<CirclePlus />} | ||
onClick={() => { | ||
onAppend({ key: '', value: '' }); | ||
}} | ||
/> | ||
</FormField> | ||
); | ||
} | ||
|
||
export default KeyValueInputField; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters