Merge branch 'main' of github.com:utopia-os/utopia-ui

This commit is contained in:
Anton Tranelis 2025-02-27 07:46:47 +00:00
commit 1bd3a8a33b
12 changed files with 133 additions and 97 deletions

View File

@ -0,0 +1,32 @@
import { render, screen, fireEvent } from '@testing-library/react'
import { describe, it, expect, beforeEach, vi } from 'vitest'
import ComboBoxInput from './ComboBoxInput'
describe('<ComboBoxInput />', () => {
let wrapper: ReturnType<typeof render>
const updateFormValue = vi.fn()
beforeEach(() => {
vi.clearAllMocks()
wrapper = render(
<ComboBoxInput
value={'option-1'}
onValueChange={updateFormValue}
options={['Option 1', 'Option 2']}
/>,
)
})
it('renders properly', () => {
expect(wrapper.container.firstChild).toMatchSnapshot()
})
describe('handleChange', () => {
it('calls updateFormValue with new value', () => {
fireEvent.change(screen.getByRole('combobox'), { target: { value: 'Option 2' } })
expect(updateFormValue).toBeCalledWith('Option 2')
})
})
})

View File

@ -1,74 +0,0 @@
import InformationCircleIcon from '@heroicons/react/24/outline/InformationCircleIcon'
import { useState } from 'react'
interface SelectBoxProps {
labelTitle?: string
labelStyle?: string
type?: string
containerStyle?: string
defaultValue: string
placeholder?: string
updateFormValue: (value: string) => void
options: { name: string; value: string }[]
labelDescription?: string
}
/**
* @category Input
*/
export function SelectBox(props: SelectBoxProps) {
const {
labelTitle,
labelDescription,
defaultValue,
containerStyle,
placeholder,
labelStyle,
options,
updateFormValue,
} = props
const [value, setValue] = useState(defaultValue || '')
const updateValue = (newValue: string) => {
updateFormValue(newValue)
setValue(newValue)
}
return (
<div className={`tw-inline-block ${containerStyle ?? ''}`}>
{labelTitle ? (
<label className={`tw-label ${labelStyle ?? ''}`}>
<div className='tw-label-text'>
{labelTitle}
{labelDescription && (
<div className='tw-tooltip tw-tooltip-right' data-tip={labelDescription}>
<InformationCircleIcon className='tw-w-4 tw-h-4' />
</div>
)}
</div>
</label>
) : (
''
)}
<select
className='tw-select tw-select-bordered tw-w-full'
value={value}
onChange={(e) => updateValue(e.target.value)}
>
<option disabled value='PLACEHOLDER'>
{placeholder}
</option>
{options.map((o, k) => {
return (
<option value={o.value || o.name} key={k}>
{o.name}
</option>
)
})}
</select>
</div>
)
}

View File

@ -0,0 +1,33 @@
import { render, screen, fireEvent } from '@testing-library/react'
import { describe, it, expect, beforeEach, vi } from 'vitest'
import { TextAreaInput } from './TextAreaInput'
describe('<TextAreaInput />', () => {
let wrapper: ReturnType<typeof render>
const updateFormValue = vi.fn()
beforeEach(() => {
vi.clearAllMocks()
wrapper = render(<TextAreaInput defaultValue={''} updateFormValue={updateFormValue} />)
})
it('renders properly', () => {
expect(wrapper.container.firstChild).toMatchSnapshot()
})
describe('handleChange', () => {
it('calls updateFormValue with new value', () => {
fireEvent.change(screen.getByRole('textbox'), { target: { value: 'test' } })
expect(updateFormValue).toBeCalledWith('test')
})
})
describe('labelTitle', () => {
it('sets label', () => {
wrapper.rerender(<TextAreaInput defaultValue={''} labelTitle='My Title' />)
expect(wrapper.container.firstChild).toMatchSnapshot()
})
})
})

View File

@ -1,7 +1,5 @@
import { useEffect, useRef, useState } from 'react'
import { useTags } from '#components/Map/hooks/useTags'
interface TextAreaProps {
labelTitle?: string
labelStyle?: string
@ -14,8 +12,6 @@ interface TextAreaProps {
updateFormValue?: (value: string) => void
}
type KeyValue = Record<string, string>
/**
* @category Input
*/
@ -33,14 +29,6 @@ export function TextAreaInput({
const ref = useRef<HTMLTextAreaElement>(null)
const [inputValue, setInputValue] = useState<string>(defaultValue)
const tags = useTags()
const values: KeyValue[] = []
tags.forEach((tag) => {
values.push({ key: tag.name, value: tag.name, color: tag.color })
})
useEffect(() => {
setInputValue(defaultValue)
}, [defaultValue])

View File

@ -0,0 +1,18 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`<ComboBoxInput /> > renders properly 1`] = `
<select
class="tw-form-select tw-block tw-w-full tw-py-2 tw-px-4 tw-border tw-border-gray-300 rounded-md tw-shadow-sm tw-text-sm focus:tw-outline-none focus:tw-ring-indigo-500 focus:tw-border-indigo-500 sm:tw-text-sm"
>
<option
value="Option 1"
>
Option 1
</option>
<option
value="Option 2"
>
Option 2
</option>
</select>
`;

View File

@ -0,0 +1,34 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`<TextAreaInput /> > labelTitle > sets label 1`] = `
<div
class="tw-form-control tw-w-full "
>
<label
class="tw-label"
>
<span
class="tw-label-text tw-text-base-content "
>
My Title
</span>
</label>
<textarea
class="tw-textarea tw-textarea-bordered tw-w-full tw-leading-5 "
placeholder=""
required=""
/>
</div>
`;
exports[`<TextAreaInput /> > renders properly 1`] = `
<div
class="tw-form-control tw-w-full "
>
<textarea
class="tw-textarea tw-textarea-bordered tw-w-full tw-leading-5 "
placeholder=""
required=""
/>
</div>
`;

View File

@ -1,3 +1,2 @@
export { TextAreaInput } from './TextAreaInput'
export { TextInput } from './TextInput'
export { SelectBox } from './SelectBox'

View File

@ -53,6 +53,13 @@ export function ItemFormPopup(props: ItemFormPopupProps) {
})
formItem.position = { type: 'Point', coordinates: [props.position.lng, props.position.lat] }
evt.preventDefault()
const name = formItem.name ? formItem.name : user?.first_name
if (!name) {
toast.error('Name is must be defined')
return
}
setSpinner(true)
formItem.text &&
@ -98,8 +105,8 @@ export function ItemFormPopup(props: ItemFormPopupProps) {
;(!props.layer.userProfileLayer || !item) &&
(await props.layer.api?.createItem!({
...formItem,
name,
id: uuid,
name: formItem.name ? formItem.name : user?.first_name,
}))
success = true
// eslint-disable-next-line no-catch-all/no-catch-all

View File

@ -32,7 +32,6 @@ import { GratitudeControl } from './Subcomponents/Controls/GratitudeControl'
import { LayerControl } from './Subcomponents/Controls/LayerControl'
import { SearchControl } from './Subcomponents/Controls/SearchControl'
import { TagsControl } from './Subcomponents/Controls/TagsControl'
import { PopupButton } from './Subcomponents/ItemPopupComponents/PopupButton'
import { TextView } from './Subcomponents/ItemPopupComponents/TextView'
import { SelectPosition } from './Subcomponents/SelectPosition'
@ -80,7 +79,9 @@ export function UtopiaMapInner({
itemId=''
rawText={'Support us building free opensource maps and help us grow 🌱☀️'}
/>
<PopupButton url={'https://opencollective.com/utopia-project'} text={'Donate'} />
<a href='https://opencollective.com/utopia-project'>
<div className='tw-btn tw-btn-sm tw-float-right'>Donate</div>
</a>
</div>
</>,
{ autoClose: false },

View File

@ -82,7 +82,6 @@ function useItemsManager(initialItems: Item[]): {
},
})
result.map((item) => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
dispatch({ type: 'ADD', item: { ...item, layer } })
return null
})

View File

@ -18,8 +18,7 @@ export interface LayerProps {
markerShape: string
markerDefaultColor: string
markerDefaultColor2?: string
// eslint-disable-next-line @typescript-eslint/no-explicit-any
api?: ItemsApi<any>
api?: ItemsApi<Item>
itemType: ItemType
userProfileLayer?: boolean
customEditLink?: string

View File

@ -12,12 +12,12 @@ export default defineConfig({
coverage: {
all: true,
include: ['src/**/*.{js,jsx,ts,tsx}'],
exclude: [...configDefaults.exclude],
exclude: [...configDefaults.exclude, 'src/**/*.cy.tsx'],
thresholds: {
lines: 0,
functions: 61,
lines: 1,
functions: 59,
branches: 62,
statements: 0,
statements: 1,
},
},
},