Skip to content

Commit

Permalink
Fix: Update LocalizedTextInput and TimeInput to include the hasWarnin…
Browse files Browse the repository at this point in the history
…g Property (#2683)

* fix: update LocalizedTextInput and TimeInput to include the hasWarning property

* fix: update visual routes specs

* chore: update readme

* chore: implement feedback
  • Loading branch information
obulaworld authored Jan 12, 2024
1 parent bca5ba9 commit 22c8c05
Show file tree
Hide file tree
Showing 12 changed files with 165 additions and 5 deletions.
6 changes: 6 additions & 0 deletions .changeset/strange-glasses-tie.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@commercetools-uikit/localized-text-input': minor
'@commercetools-uikit/time-input': minor
---

- Update the LocalizedTextInput and the TimeInput to include the hasWarning props.
2 changes: 2 additions & 0 deletions packages/components/inputs/localized-text-input/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,9 @@ export default Example;
| `placeholder` | `Record` | | | Placeholders for each language. Object of the same shape as `value`. |
| `horizontalConstraint` | `union`<br/>Possible values:<br/>`, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 'scale', 'auto'` | | `'scale'` | Horizontal size limit of the input fields. |
| `hasError` | `boolean` | | | Will apply the error state to each input without showing any error message. |
| `hasWarning` | `boolean` | | | Indicates the input field has a warning |
| `errors` | `Record` | | | Used to show errors underneath the inputs of specific currencies. Pass an object whose key is a currency and whose value is the error to show for that key. |
| `warnings` | `Record` | | | A map of warnings. |

## `data-*` props

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -263,3 +263,48 @@ describe('when the error is on the selected language', () => {
expect(getByText('Some error')).toBeInTheDocument();
});
});

describe('when every field has a warning', () => {
const warnings = {
en: 'This field has a warning',
fr: 'Ce champ contient un avertissement',
};
it('should open all fields and render warnings', () => {
const { getByLabelText, getByText } = renderLocalizedTextInput({
warnings,
});
expect(getByLabelText('EN')).toBeInTheDocument();
expect(getByLabelText('FR')).toBeInTheDocument();
expect(getByText(warnings.en)).toBeInTheDocument();
expect(getByText(warnings.fr)).toBeInTheDocument();
});
});

describe('when the warning is not on the selected language', () => {
const warnings = {
en: '',
fr: 'Ce champ contient un avertissement',
};
it('should open all fields and render warnings', () => {
const { getByLabelText, getByText } = renderLocalizedTextInput({
warnings,
});
expect(getByLabelText('EN')).toBeInTheDocument();
expect(getByLabelText('FR')).toBeInTheDocument();
expect(getByText(warnings.fr)).toBeInTheDocument();
});
});

describe('when the warnings is on the selected language', () => {
it('should display the warnings without expanding', () => {
const { getByLabelText, getByText, queryByLabelText } =
renderLocalizedTextInput({
warnings: {
en: 'Some warning',
},
});
expect(getByLabelText('EN')).toBeInTheDocument();
expect(queryByLabelText('FR')).not.toBeInTheDocument();
expect(getByText('Some warning')).toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
object,
} from '@storybook/addon-knobs/react';
import Constraints from '@commercetools-uikit/constraints';
import { ErrorMessage } from '@commercetools-uikit/messages';
import { ErrorMessage, WarningMessage } from '@commercetools-uikit/messages';
import Section from '../../../../../docs/.storybook/decorators/section';
import Readme from '../README.md';
import LocalizedTextInput from './localized-text-input';
Expand All @@ -29,6 +29,7 @@ storiesOf('Components|Inputs', module)
// We do this by changing the key.
const key = defaultExpandLanguages ? 'yes' : 'no';
const errors = object('errors', { en: '', de: '', 'nan-Hant-TW': '' });
const warnings = object('warnings', { en: '', de: '', 'nan-Hant-TW': '' });
return (
<Section>
<Value
Expand Down Expand Up @@ -68,6 +69,7 @@ storiesOf('Components|Inputs', module)
7
)}
hasError={boolean('hasError', false)}
hasWarning={boolean('hasWarning', false)}
errors={
Object.values(errors).some((error) => error.length > 0)
? Object.entries(errors).reduce((acc, [language, error]) => {
Expand All @@ -77,6 +79,20 @@ storiesOf('Components|Inputs', module)
}, {})
: undefined
}
warnings={
Object.values(warnings).some((warning) => warning.length > 0)
? Object.entries(warnings).reduce(
(acc, [language, warning]) => {
if (warning.length === 0) return acc;
acc[language] = (
<WarningMessage>{warning}</WarningMessage>
);
return acc;
},
{}
)
: undefined
}
data-test="foo"
/>
)}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
type FocusEventHandler,
type ChangeEventHandler,
type ReactNode,
useCallback,
} from 'react';
import { FormattedMessage } from 'react-intl';
Expand All @@ -13,6 +14,7 @@ import {
sortLanguages,
createLocalizedDataAttributes,
getHasErrorOnRemainingLanguages,
getHasWarningOnRemainingLanguages,
isTouched,
omitEmptyTranslations,
isEmpty,
Expand Down Expand Up @@ -119,10 +121,18 @@ export type TLocalizedTextInputProps = {
* Will apply the error state to each input without showing any error message.
*/
hasError?: boolean;
/**
* Indicates the input field has a warning
*/
hasWarning?: boolean;
/**
* Used to show errors underneath the inputs of specific currencies. Pass an object whose key is a currency and whose value is the error to show for that key.
*/
errors?: Record<string, string>;
/**
* A map of warnings.
*/
warnings?: Record<string, ReactNode>;
};

export type TLocalizedInputProps = {
Expand Down Expand Up @@ -150,6 +160,14 @@ export type TLocalizedInputProps = {
isDisabled?: boolean;
isReadOnly?: boolean;
hasError?: boolean;
/**
* Indicates the input field has a warning
*/
hasWarning?: boolean;
/**
* HTML node to display warning
*/
warning?: ReactNode;
placeholder?: string;
};

Expand Down Expand Up @@ -240,10 +258,14 @@ const LocalizedTextInput = (props: TLocalizedTextInputProps) => {
props.hasError ||
getHasErrorOnRemainingLanguages(props.errors, props.selectedLanguage);

if (hasErrorInRemainingLanguages) {
const hasWarningInRemainingLanguages =
props.hasWarning ||
getHasWarningOnRemainingLanguages(props.warnings, props.selectedLanguage);

if (hasErrorInRemainingLanguages || hasWarningInRemainingLanguages) {
// this update within render replaces the old `getDerivedStateFromProps` functionality
// https://reactjs.org/docs/hooks-faq.html#how-do-i-implement-getderivedstatefromprops
if (hasErrorInRemainingLanguages !== areLanguagesExpanded) {
if (!areLanguagesExpanded) {
toggleLanguages();
}
}
Expand Down Expand Up @@ -296,12 +318,18 @@ const LocalizedTextInput = (props: TLocalizedTextInputProps) => {
hasError={Boolean(
props.hasError || (props.errors && props.errors[language])
)}
hasWarning={Boolean(
props.hasWarning ||
(props.warnings && props.warnings[language])
)}
warning={props.warnings && props.warnings[language]}
{...createLocalizedDataAttributes(props, language)}
/* ARIA */
aria-invalid={props['aria-invalid']}
aria-errormessage={props['aria-errormessage']}
/>
{props.errors && props.errors[language]}
{props.warnings && props.warnings[language]}
</Stack>
</div>
);
Expand All @@ -311,7 +339,12 @@ const LocalizedTextInput = (props: TLocalizedTextInputProps) => {
<LocalizedInputToggle
isOpen={areLanguagesExpanded}
onClick={onLocalizedInputToggle}
isDisabled={areLanguagesExpanded && hasErrorInRemainingLanguages}
isDisabled={
areLanguagesExpanded &&
Boolean(
hasErrorInRemainingLanguages || hasWarningInRemainingLanguages
)
}
remainingLocalizations={languages.length - 1}
/>
)}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
LocalizedTextInput,
ErrorMessage,
WarningMessage,
} from '@commercetools-frontend/ui-kit';
import { Suite, Spec } from '../../../../../test/percy';

Expand Down Expand Up @@ -104,5 +105,32 @@ export const component = () => (
hasError={true}
/>
</Spec>
<Spec label="when there is a warning for a specific language (first one)">
<LocalizedTextInput
value={value}
onChange={() => {}}
selectedLanguage="en"
horizontalConstraint={7}
warnings={{ en: <WarningMessage>foo</WarningMessage> }}
/>
</Spec>
<Spec label="when there is a warning for a specific language (second one)">
<LocalizedTextInput
value={value}
onChange={() => {}}
selectedLanguage="en"
horizontalConstraint={7}
warnings={{ de: <WarningMessage>foo</WarningMessage> }}
/>
</Spec>
<Spec label="when there is a general warning">
<LocalizedTextInput
value={value}
onChange={() => {}}
selectedLanguage="en"
horizontalConstraint={7}
hasWarning={true}
/>
</Spec>
</Suite>
);
1 change: 1 addition & 0 deletions packages/components/inputs/time-input/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export default Example;
| `isAutofocussed` | `boolean` | | | Focus the input on initial render |
| `isDisabled` | `boolean` | | | Indicates that the input cannot be modified (e.g not authorized, or changes currently saving). |
| `placeholder` | `string` | | | Placeholder text for the input |
| `hasWarning` | `boolean` | | | Indicates the input field has a warning |
| `hasError` | `boolean` | | | Indicates if the input has invalid values |
| `isReadOnly` | `boolean` | | | Indicates that the field is displaying read-only content |

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ const getClockIconContainerColor = (props: TTimeInputProps) => {
if (props.isDisabled) {
return designTokens.borderColorForInputWhenDisabled;
}
if (props.hasWarning) {
return designTokens.borderColorForInputWhenWarning;
}
if (props.hasError) {
return designTokens.borderColorForInputWhenError;
}
Expand All @@ -41,6 +44,9 @@ const getClockIconContainerFontColor = (props: TTimeInputProps) => {
if (props.isDisabled) {
return designTokens.fontColorForInputWhenDisabled;
}
if (props.hasWarning) {
return designTokens.fontColorForInputWhenWarning;
}
if (props.hasError) {
return designTokens.fontColorForInputWhenError;
}
Expand Down Expand Up @@ -82,6 +88,9 @@ const getInputContainerBorderColor = (props: TTimeInputProps) => {
if (props.isDisabled) {
return designTokens.borderColorForInputWhenDisabled;
}
if (props.hasWarning) {
return designTokens.borderColorForInputWhenWarning;
}
if (props.hasError) {
return designTokens.borderColorForInputWhenError;
}
Expand All @@ -95,6 +104,9 @@ const getInputContainerFontColor = (props: TTimeInputProps) => {
if (props.isDisabled) {
return designTokens.fontColorForInputWhenDisabled;
}
if (props.hasWarning) {
return designTokens.fontColorForInputWhenWarning;
}
if (props.hasError) {
return designTokens.fontColorForInputWhenError;
}
Expand Down Expand Up @@ -164,7 +176,7 @@ const getInputContainerStyles = (props: TTimeInputProps) => {
}
}
`,
props.hasError &&
(props.hasError || props.hasWarning) &&
css`
box-shadow: ${designTokens.boxShadowForDatetimeInputWhenHovered};
`,
Expand Down
2 changes: 2 additions & 0 deletions packages/components/inputs/time-input/src/time-input-body.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ type TTimeInputBodyProps = TTimeInputProps & {
type TClearSectionProps = {
isDisabled?: boolean;
hasError?: boolean;
hasWarning?: boolean;
isReadOnly?: boolean;
onClear: (
event: MouseEvent<HTMLButtonElement> | KeyboardEvent<HTMLButtonElement>
Expand Down Expand Up @@ -79,6 +80,7 @@ const TimeInputBody = forwardRef<HTMLInputElement, TTimeInputBodyProps>(
<ClearSection
isDisabled={props.isDisabled}
hasError={props.hasError}
hasWarning={props.hasWarning}
isReadOnly={props.isReadOnly}
onClear={props.onClear}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ storiesOf('Components|Inputs', module)
onFocus={action('onFocus')}
onBlur={action('onBlur')}
hasError={boolean('hasError', false)}
hasWarning={boolean('hasWarning', false)}
horizontalConstraint={select(
'horizontalConstraint',
Constraints.getAcceptedMaxPropValues(3),
Expand Down
6 changes: 6 additions & 0 deletions packages/components/inputs/time-input/src/time-input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,13 +94,18 @@ export type TTimeInputProps = {
* Placeholder text for the input
*/
placeholder?: string;
/**
* Indicates the input field has a warning
*/
hasWarning?: boolean;
/**
* Indicates if the input has invalid values
*/
hasError?: boolean;
/**
* Indicates that the field is displaying read-only content
*/

isReadOnly?: boolean;
};

Expand Down Expand Up @@ -195,6 +200,7 @@ const TimeInput = (props: TTimeInputProps) => {
isDisabled={props.isDisabled}
isReadOnly={props.isReadOnly}
hasError={props.hasError}
hasWarning={props.hasWarning}
onClear={handleClear}
placeholder={
typeof props.placeholder === 'string'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,13 @@ export const component = () => (
isReadOnly={true}
/>
</Spec>
<Spec label="with warning">
<TimeInput
value={null}
onChange={() => {}}
horizontalConstraint={7}
hasWarning={true}
/>
</Spec>
</Suite>
);

1 comment on commit 22c8c05

@vercel
Copy link

@vercel vercel bot commented on 22c8c05 Jan 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.