Skip to main content

StartStop

StartStop is a set of controls for selecting a start and stop date. The shape of the options being passed in will determine whether a PlusMinus control appears to specify a duration, age, or some other value.

Options can also specify a milestoneDate which will be used to calculate a date based on the selected duration.

The StartStop component supports minimal schema validation with yup & Formik. It is recommended to perform complex validation with the validate callback. A helper function (handleStartStopBlur) is provided to validate the start and stop values that can be used as an onBlur callback.

Result
Loading...
Live Editor
const startOptions = [
	{
		id: 'Start',
		defaultValue: '01/01/2000',
		label: 'Start (01/01/2000)',
	},
	{
		id: 'ClientDeath',
		defaultValue: '05/01/2055',
		label: 'Death of client (05/01/2055)',
	},
	{
		id: 'CalendarYear',
		defaultValue: 2050,
		label: 'Calendar Year',
		numberFieldProps: { minValue: 2045, maxValue: 2055 },
	},
	{
		id: 'ClientIs',
		defaultValue: 50,
		label: 'When client is',
		milestoneDate: '05/30/1980',
		descriptionPrefix: 'Actual year:',
		numberFieldProps: { minValue: 18, maxValue: 100 },
	},
];

const stopOptions = [
	{
		id: 'Stop',
		defaultValue: '12/31/2022',
		label: 'Stop (12/31/2022)',
	},
	{
		id: 'SpouseDeath',
		defaultValue: '02/23/2067',
		label: 'Death of spouse (02/23/2067)',
	},
	{
		id: 'Duration',
		label: 'Duration',
		defaultValue: 4,
	},
	{
		id: 'Retirement',
		defaultValue: '05/05/2030',
		label: 'Retirement (05/05/2030)',
	},
	{
		id: 'CalendarYear',
		defaultValue: 2050,
		label: 'Calendar Year',
		numberFieldProps: { minValue: 2045, maxValue: 2055 },
	},
	{
		id: 'SpouseIs',
		defaultValue: 65,
		label: 'When spouse is',
		milestoneDate: '05/30/1978',
		descriptionPrefix: 'Actual year:',
		numberFieldProps: { minValue: 18, maxValue: 100 },
	},
];

function Example() {
	const [startValue, setStartValue] = useState('');
	const [stopValue, setStopValue] = useState();
	const [errorText, setErrorText] = useState();

	const handleChange = (payload) => {
		console.log('change', payload);

		setStartValue(payload.startValue);
		setStopValue(payload.stopValue);
	};

	const handleBlur = () => {
		setErrorText(handleStartStopBlur(startValue, stopValue, startOptions, stopOptions));
	};

	return (
		<StartStop
			errorText={errorText}
			isInvalid={!!errorText}
			onStartBlur={handleBlur}
			onStartChange={handleChange}
			onStopBlur={handleBlur}
			onStopChange={handleChange}
			startOptions={startOptions}
			startValue={startValue}
			stopOptions={stopOptions}
			stopValue={stopValue}
		/>
	);
}

render(<Example />);

Validation

The StartStop component supports minimal schema validation with yup & Formik. It is recommended to perform complex validation with the validate callback. A helper function (handleStartStopBlur) is provided to validate the start and stop values along with the options used.

Result
Loading...
Live Editor
const startOptions = [
	{
		id: 'ClientDeath',
		defaultValue: '05/01/2055',
		label: 'Death of client (05/01/2055)',
	},
	{
		id: 'CalendarYear',
		defaultValue: 2050,
		label: 'Calendar Year',
	},
	{
		id: 'ClientIs',
		defaultValue: 50,
		label: 'When client is',
		milestoneDate: '05/30/1980',
		descriptionPrefix: 'Actual year:',
	},
];

const stopOptions = [
	{
		id: 'Retirement',
		defaultValue: '05/05/2030',
		label: 'Retirement (05/05/2030)',
	},
	{
		id: 'CalendarYear',
		defaultValue: 2050,
		label: 'Calendar Year',
	},
	{
		id: 'SpouseIs',
		defaultValue: 65,
		label: 'When spouse is',
		milestoneDate: '05/30/1978',
		descriptionPrefix: 'Actual year:',
	},
];

function Example() {
	return (
		<Form
			initialValues={{}}
			onSubmit={(values, { setSubmitting }) => {
				console.log(values);
				setSubmitting(false);
			}}
			validateOnBlur
			validationSchema={object().shape({
				startstop: object().shape({
					startValue: object().shape({
						type: string().required('Start type is required'),
						value: mixed()
							.when('type', {
								is: 'ClientIs',
								then: number()
									.label('Age')
									.min(25, 'Age must be at least 25')
									.max(65, 'Age must be at most 65'),
							})
							.when('type', {
								is: 'CalendarYear',
								then: number()
									.label('Calendar Year')
									.min(2024, 'Year must be at least 2024')
									.max(2100, 'Year must be at most 2100'),
							}),
					}),
					stopValue: object().shape({
						type: string().required('Stop type is required'),
						value: mixed().when('type', {
							is: 'CalendarYear',
							then: number()
								.label('Calendar Year')
								.min(2024, 'Year must be at least 2024')
								.max(2100, 'Year must be at most 2100'),
						}),
					}),
				}),
			})}
		>
			<FormField
				validate={(values) =>
					values &&
					handleStartStopBlur(values.startValue, values.stopValue, startOptions, stopOptions)
				}
			>
				<StartStop
					name="startstop"
					label="StartStop"
					startOptions={startOptions}
					stopOptions={stopOptions}
				/>
			</FormField>
		</Form>
	);
}

render(
	<div data-testid="pw-startstop-within-form">
		<Example />
	</div>,
);

Select and NumberField Props

The startSelectProps, startNumberFieldProps, stopSelectProps, and stopNumberFieldProps pass additional props to the underlying Select and NumberField components.

Additionally, each option in the startOptions and stopOptions arrays can also include a selectProps and the numberFieldProps object.

Note: Props defined in the option will always override over props defined in startSelectProps, startNumberFieldProps, stopSelectProps, and stopNumberFieldProps, with the exception of description which will be combined together.

Result
Loading...
Live Editor
const startOptions = [
	{
		id: 'CalendarYear',
		defaultValue: 2050,
		label: 'Calendar Year',
		selectProps: { description: 'Option 1 description from selectProps.' },
		numberFieldProps: { minValue: 2045, maxValue: 2055, description: 'Option 1 description from numberFieldProps.' },
	},
	{
		id: 'ClientIs',
		defaultValue: 50,
		label: 'When client is',
		milestoneDate: '05/30/1980',
		descriptionPrefix: 'Actual year:',
		selectProps: { description: 'Option 2 description from selectProps.' },
		numberFieldProps: { minValue: 18, maxValue: 100, description: 'Option 2 description from numberFieldProps.' },
	},
];

const stopOptions = [
	{
		id: 'CalendarYear',
		defaultValue: 2050,
		label: 'Calendar Year',
		selectProps: { description: 'Description 1 from selectProps.' },
		numberFieldProps: { minValue: 2045, maxValue: 2055, description: 'Description 1 from numberFieldProps.' },
	},
	{
		id: 'SpouseIs',
		defaultValue: 65,
		label: 'When spouse is',
		milestoneDate: '05/30/1978',
		descriptionPrefix: 'Actual year:',
		selectProps: { description: 'Option 2 description from selectProps.' },
		numberFieldProps: { minValue: 18, maxValue: 100, description: 'Option 2 description from numberFieldProps.' },
	},
];

function Example() {
	const [startValue, setStartValue] = useState();
	const [stopValue, setStopValue] = useState();
	const [errorText, setErrorText] = useState();

	const handleChange = (payload) => {
		console.log('change', payload);

		setStartValue(payload.startValue);
		setStopValue(payload.stopValue);
	};

	const handleBlur = () => {
		setErrorText(handleStartStopBlur(startValue, stopValue, startOptions, stopOptions));
	};

	return (
		<StartStop
			errorText={errorText}
			isInvalid={!!errorText}
			onStartBlur={handleBlur}
			onStartChange={handleChange}
			onStopBlur={handleBlur}
			onStopChange={handleChange}
			startOptions={startOptions}
			startValue={startValue}
			stopOptions={stopOptions}
			stopValue={stopValue}
			startSelectProps={{ description: "Description from startSelectProps" }}
			startNumberFieldProps={{ description: "Description from startNumberFieldProps" }}
			stopSelectProps={{ description: "Description from stopSelectProps" }}
			stopNumberFieldProps={{ description: "Description from stopNumberFieldProps" }}
		/>
	);
}

render(<Example />);

Analytics

The StartStop component is trackable through Kyber Analytics. This is the default analytics config.

export default {
value: 'StartStop',
actions: {
onStartChange: { type: 'STARTSTOP_START_CHANGE', payload: 'Change' },
onStopChange: { type: 'STARTSTOP_STOP_CHANGE', payload: 'Change' },
},
};

Props