Skip to content

Commit

Permalink
feat: clamp duration for DurationInput
Browse files Browse the repository at this point in the history
  • Loading branch information
Grafikart committed Oct 27, 2023
1 parent 357c85c commit bc92a5c
Show file tree
Hide file tree
Showing 7 changed files with 65 additions and 45 deletions.
20 changes: 6 additions & 14 deletions src/components/duration/duration.scss
Original file line number Diff line number Diff line change
@@ -1,17 +1,9 @@
.input-field {
width: 100px;
margin-right: 10px
}
.field-group {
.duration-fields,
.duration-field {
display: flex;
align-items: center;
gap: 1rem;
}

.input-field-group {
display: flex;
align-items: first baseline;
margin: 5px;
.duration-field {
gap: .5rem;
}

.input-label {
margin-right: 5px;
}
11 changes: 7 additions & 4 deletions src/components/duration/duration.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,13 @@ function Duration(props: LunaticComponentProps<'Duration'>) {
description={description}
handleChange={handleChange}
>
<div className="container">
{label}
<DurationInput value={value} format={format} onChange={onChange} />
</div>
<DurationInput
id={id}
label={label}
value={value}
format={format}
onChange={onChange}
/>
</LunaticComponent>
);
}
Expand Down
42 changes: 25 additions & 17 deletions src/components/duration/durationInput.tsx
Original file line number Diff line number Diff line change
@@ -1,45 +1,53 @@
import { Fragment, useState } from 'react';
import { type ReactNode, useState } from 'react';
import { objectKeys } from '../../utils/object';
import { formatDuration } from './formatDuration';
import { getDurationFromValue } from './getDurationFromValue';
import { type Formats, labelByUnit, propsByUnit } from './durationUtils';
import {
clampDuration,
type Formats,
labelByUnit,
propsByUnit,
} from './durationUtils';
import classnames from 'classnames';

const DurationInput = ({
value,
format,
onChange,
}: {
type Props = {
label?: ReactNode;
id?: string;
value: string | null;
format: Formats;
onChange: (s: string | null) => void;
}) => {
};

const DurationInput = ({ value, format, onChange, label }: Props) => {
// We need to keep an internal state since one field can be empty (null value in duration)
// but we still send "0" and we don't want the field to display "0"
const [duration, setDuration] = useState(getDurationFromValue(value, format));

// Generate handler for a specific unit field (year, month...)
const changeHandler =
(unit: string) =>
(unit: 'hours' | 'minutes' | 'months' | 'years') =>
(e: {
// CheckValidity function is used to apply constraints to a field Ex: (min, max)
target: { valueAsNumber: number; checkValidity: () => boolean };
}) => {
if (!e.target.checkValidity()) {
return;
}
const fieldValue = Number.isNaN(e.target.valueAsNumber)
? null
: e.target.valueAsNumber;
const fieldValue = clampDuration(
Number.isNaN(e.target.valueAsNumber) ? null : e.target.valueAsNumber,
unit
);
const newDuration = { ...duration, [unit]: fieldValue };
onChange(formatDuration(newDuration));
setDuration(newDuration);
};

return (
<div className="field-group">
<div className="input-field-group">
<fieldset className={classnames('lunatic-input')}>
{label && <legend>{label}</legend>}
<div className="duration-fields">
{objectKeys(duration).map((unit) => (
<Fragment key={unit}>
<div className="duration-field" key={unit}>
<label htmlFor={`${unit}Input`} className="input-label">
{labelByUnit[unit]}
</label>
Expand All @@ -51,10 +59,10 @@ const DurationInput = ({
onChange={changeHandler(unit)}
{...propsByUnit[unit]}
/>
</Fragment>
</div>
))}
</div>
</div>
</fieldset>
);
};

Expand Down
25 changes: 21 additions & 4 deletions src/components/duration/durationUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ export type DurationValue = {
export type Formats = 'PTnHnM' | 'PnYnM';

export const propsByUnit = {
hours: { min: '0', max: '23' },
minutes: { min: '0', max: '59' },
months: { min: '0', max: '11' },
years: { min: '0' },
hours: { min: 0, max: 23, size: 2, style: { width: '2em' } },
minutes: { min: 0, max: 59, size: 2, style: { width: '2em' } },
months: { min: 0, max: 11, size: 2, style: { width: '2em' } },
years: { min: 0, size: 4, style: { width: '4em' } },
};

export const labelByUnit = {
Expand All @@ -20,3 +20,20 @@ export const labelByUnit = {
months: 'Mois : ',
years: 'Années :',
};

export function clampDuration(
value: number | null,
unit: keyof typeof propsByUnit
) {
if (value === null) {
return value;
}
const props = propsByUnit[unit];
if (value < props.min) {
return props.min;
}
if ('max' in props && value > props.max) {
return props.max;
}
return value;
}
8 changes: 4 additions & 4 deletions src/stories/duration/duration.stories.jsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import React from 'react';
import defaultArgTypes from '../utils/default-arg-types';
import Orchestrator from '../utils/orchestrator';
import source from './source';
import source1 from './source1';
import moisSource from './mois';
import timeSource from './time';

const stories = {
title: 'Components/Duration',
Expand All @@ -15,11 +15,11 @@ export default stories;
const Template = (args) => <Orchestrator {...args} />;
export const DateDuration = Template.bind({});

DateDuration.args = { id: 'durationAnnéesMois', source };
DateDuration.args = { id: 'durationAnnéesMois', source: moisSource };

export const TimeDuration = Template.bind({});

TimeDuration.args = {
id: 'durationHeureMinute',
source: source1,
source: timeSource,
};
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"page": "1",
"maxPage": "1",
"label": {
"value": "\"➡ 1. \" || \"Duration \"",
"value": "\"➡ 1. \" || \"Duration (format: PnYnM) \"",
"type": "VTL|MD"
},
"description": { "value": "\"Description\"", "type": "VTL|MD" },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"page": "1",
"maxPage": "1",
"label": {
"value": "\"➡ 1. \" || \"Duration \"",
"value": "\"➡ 1. \" || \"Duration (format: PTnHnM) \"",
"type": "VTL|MD"
},
"description": { "value": "\"Description\"", "type": "VTL|MD" },
Expand Down

0 comments on commit bc92a5c

Please sign in to comment.