mirror of
https://github.com/langgenius/dify.git
synced 2026-02-13 14:34:54 +08:00
feat(time-picker): add showTimezone prop with comprehensive tests
- Add showTimezone prop to TimePickerProps for optional inline timezone display - Integrate TimezoneLabel component into TimePicker when showTimezone=true - Add 6 comprehensive test cases covering all showTimezone scenarios: * Default behavior (no timezone label) * Explicit disable with showTimezone=false * Enable with showTimezone=true * Inline prop correctly passed * No display when timezone is missing * Correct styling classes applied - Update trigger-schedule panel to use showTimezone prop - All 15 tests passing with good coverage
This commit is contained in:
parent
af6dae3498
commit
4dfb8b988c
@ -28,6 +28,15 @@ jest.mock('@/app/components/base/portal-to-follow-elem', () => ({
|
||||
|
||||
jest.mock('./options', () => () => <div data-testid="time-options" />)
|
||||
jest.mock('./header', () => () => <div data-testid="time-header" />)
|
||||
jest.mock('@/app/components/base/timezone-label', () => {
|
||||
return function MockTimezoneLabel({ timezone, inline, className }: { timezone: string, inline?: boolean, className?: string }) {
|
||||
return (
|
||||
<span data-testid="timezone-label" data-timezone={timezone} data-inline={inline} className={className}>
|
||||
UTC+8
|
||||
</span>
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
describe('TimePicker', () => {
|
||||
const baseProps = {
|
||||
@ -92,4 +101,86 @@ describe('TimePicker', () => {
|
||||
expect(isDayjsObject(emitted)).toBe(true)
|
||||
expect(emitted?.utcOffset()).toBe(dayjs().tz('America/New_York').utcOffset())
|
||||
})
|
||||
|
||||
describe('Timezone Label Integration', () => {
|
||||
test('should not display timezone label by default', () => {
|
||||
render(
|
||||
<TimePicker
|
||||
{...baseProps}
|
||||
value="12:00 AM"
|
||||
timezone="Asia/Shanghai"
|
||||
/>,
|
||||
)
|
||||
|
||||
expect(screen.queryByTestId('timezone-label')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
test('should not display timezone label when showTimezone is false', () => {
|
||||
render(
|
||||
<TimePicker
|
||||
{...baseProps}
|
||||
value="12:00 AM"
|
||||
timezone="Asia/Shanghai"
|
||||
showTimezone={false}
|
||||
/>,
|
||||
)
|
||||
|
||||
expect(screen.queryByTestId('timezone-label')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
test('should display timezone label when showTimezone is true', () => {
|
||||
render(
|
||||
<TimePicker
|
||||
{...baseProps}
|
||||
value="12:00 AM"
|
||||
timezone="Asia/Shanghai"
|
||||
showTimezone={true}
|
||||
/>,
|
||||
)
|
||||
|
||||
const timezoneLabel = screen.getByTestId('timezone-label')
|
||||
expect(timezoneLabel).toBeInTheDocument()
|
||||
expect(timezoneLabel).toHaveAttribute('data-timezone', 'Asia/Shanghai')
|
||||
})
|
||||
|
||||
test('should pass inline prop to timezone label', () => {
|
||||
render(
|
||||
<TimePicker
|
||||
{...baseProps}
|
||||
value="12:00 AM"
|
||||
timezone="America/New_York"
|
||||
showTimezone={true}
|
||||
/>,
|
||||
)
|
||||
|
||||
const timezoneLabel = screen.getByTestId('timezone-label')
|
||||
expect(timezoneLabel).toHaveAttribute('data-inline', 'true')
|
||||
})
|
||||
|
||||
test('should not display timezone label when showTimezone is true but timezone is not provided', () => {
|
||||
render(
|
||||
<TimePicker
|
||||
{...baseProps}
|
||||
value="12:00 AM"
|
||||
showTimezone={true}
|
||||
/>,
|
||||
)
|
||||
|
||||
expect(screen.queryByTestId('timezone-label')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
test('should apply shrink-0 and text-xs classes to timezone label', () => {
|
||||
render(
|
||||
<TimePicker
|
||||
{...baseProps}
|
||||
value="12:00 AM"
|
||||
timezone="Europe/London"
|
||||
showTimezone={true}
|
||||
/>,
|
||||
)
|
||||
|
||||
const timezoneLabel = screen.getByTestId('timezone-label')
|
||||
expect(timezoneLabel).toHaveClass('shrink-0', 'text-xs')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -19,6 +19,7 @@ import Header from './header'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { RiCloseCircleFill, RiTimeLine } from '@remixicon/react'
|
||||
import cn from '@/utils/classnames'
|
||||
import TimezoneLabel from '@/app/components/base/timezone-label'
|
||||
|
||||
const to24Hour = (hour12: string, period: Period) => {
|
||||
const normalized = Number.parseInt(hour12, 10) % 12
|
||||
@ -36,6 +37,7 @@ const TimePicker = ({
|
||||
minuteFilter,
|
||||
popupClassName,
|
||||
notClearable = false,
|
||||
showTimezone = false,
|
||||
}: TimePickerProps) => {
|
||||
const { t } = useTranslation()
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
@ -214,6 +216,9 @@ const TimePicker = ({
|
||||
onClick={handleClickTrigger}
|
||||
>
|
||||
{inputElem}
|
||||
{showTimezone && timezone && (
|
||||
<TimezoneLabel timezone={timezone} inline className="shrink-0 text-xs" />
|
||||
)}
|
||||
<RiTimeLine className={cn(
|
||||
'h-4 w-4 shrink-0 text-text-quaternary',
|
||||
isOpen ? 'text-text-secondary' : 'group-hover:text-text-secondary',
|
||||
|
||||
@ -65,6 +65,8 @@ export type TimePickerProps = {
|
||||
minuteFilter?: (minutes: string[]) => string[]
|
||||
popupClassName?: string
|
||||
notClearable?: boolean
|
||||
/** Show timezone label inline with the time picker */
|
||||
showTimezone?: boolean
|
||||
}
|
||||
|
||||
export type TimePickerFooterProps = {
|
||||
|
||||
@ -12,7 +12,6 @@ import NextExecutionTimes from './components/next-execution-times'
|
||||
import MonthlyDaysSelector from './components/monthly-days-selector'
|
||||
import OnMinuteSelector from './components/on-minute-selector'
|
||||
import Input from '@/app/components/base/input'
|
||||
import TimezoneLabel from '@/app/components/base/timezone-label'
|
||||
import useConfig from './use-config'
|
||||
|
||||
const i18nPrefix = 'workflow.nodes.triggerSchedule'
|
||||
@ -70,24 +69,22 @@ const Panel: FC<NodePanelProps<ScheduleTriggerNodeType>> = ({
|
||||
<label className="mb-2 block text-xs font-medium text-gray-500">
|
||||
{t('workflow.nodes.triggerSchedule.time')}
|
||||
</label>
|
||||
<div className="flex items-center gap-2">
|
||||
<TimePicker
|
||||
notClearable={true}
|
||||
timezone={inputs.timezone}
|
||||
value={inputs.visual_config?.time || '12:00 AM'}
|
||||
onChange={(time) => {
|
||||
if (time) {
|
||||
const timeString = time.format('h:mm A')
|
||||
handleTimeChange(timeString)
|
||||
}
|
||||
}}
|
||||
onClear={() => {
|
||||
handleTimeChange('12:00 AM')
|
||||
}}
|
||||
placeholder={t('workflow.nodes.triggerSchedule.selectTime')}
|
||||
/>
|
||||
<TimezoneLabel timezone={inputs.timezone} />
|
||||
</div>
|
||||
<TimePicker
|
||||
notClearable={true}
|
||||
timezone={inputs.timezone}
|
||||
value={inputs.visual_config?.time || '12:00 AM'}
|
||||
onChange={(time) => {
|
||||
if (time) {
|
||||
const timeString = time.format('h:mm A')
|
||||
handleTimeChange(timeString)
|
||||
}
|
||||
}}
|
||||
onClear={() => {
|
||||
handleTimeChange('12:00 AM')
|
||||
}}
|
||||
placeholder={t('workflow.nodes.triggerSchedule.selectTime')}
|
||||
showTimezone={true}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user