Skip to content

Commit

Permalink
Migrate input-utils to TypeScript (#1867)
Browse files Browse the repository at this point in the history
* refactor(packages/input-util): migrate to TypeScript

* refactor(multiline-input): remove superfluous `@types` dependency

* refactor: update signature

* fix(input-utils): make `theme` optional

* style(index): rename to `.ts`

* refactor: revert removal of ref

* fix(input-utils): ensure `remainingLocalizations` is optional

* chore: add changeset

* fix(multiline-input): introduce theme and forward to `getStyles`

* fix(input-toggle): set `onClick` to required

* refactor(input-utils/src/multiline-input/multiline-input.tsx): assign type to `useRef`

* refactor(multiline-input): extend useRef, add warning
  • Loading branch information
adnasa authored Apr 27, 2021
1 parent 494094a commit 4acb66d
Show file tree
Hide file tree
Showing 16 changed files with 142 additions and 107 deletions.
5 changes: 5 additions & 0 deletions .changeset/empty-ladybugs-remain.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@commercetools-uikit/input-utils': patch
---

migrate `input-utils` to TypeScript
Original file line number Diff line number Diff line change
@@ -1,21 +1,39 @@
import React from 'react';
import PropTypes from 'prop-types';
import type { MessageDescriptor } from 'react-intl';
import React, { MouseEvent, KeyboardEvent } from 'react';
import { useIntl } from 'react-intl';
import FlatButton from '@commercetools-uikit/flat-button';
import { WorldIcon } from '@commercetools-uikit/icons';
import messages from '../messages/localized-input';

const LocalizedInputToggle = (props) => {
const intl = useIntl();
type TLocalizedInputToggleProps = {
icon?: React.ReactElement;
isOpen?: boolean;
onClick: (
event: MouseEvent<HTMLButtonElement> | KeyboardEvent<HTMLButtonElement>
) => void;
isDisabled?: boolean;
showMessage: string | MessageDescriptor;
hideMessage: string | MessageDescriptor;
remainingLocalizations?: number;
};

const defaultProps: Pick<
TLocalizedInputToggleProps,
'showMessage' | 'hideMessage'
> = {
hideMessage: messages.hide,
showMessage: messages.show,
};

const LocalizedInputToggle = (props: TLocalizedInputToggleProps) => {
const intl = useIntl();
const labelMessage = props.isOpen ? props.hideMessage : props.showMessage;
const label =
typeof labelMessage === 'string'
? labelMessage
: intl.formatMessage(labelMessage, {
remainingLanguages: props.remainingLocalizations,
});

return (
<FlatButton
icon={props.icon ? props.icon : <WorldIcon />}
Expand All @@ -26,27 +44,6 @@ const LocalizedInputToggle = (props) => {
);
};

const intlMessageShape = PropTypes.shape({
id: PropTypes.string.isRequired,
description: PropTypes.string,
defaultMessage: PropTypes.string.isRequired,
});

LocalizedInputToggle.defaultProps = defaultProps;
LocalizedInputToggle.displayName = 'LocalizedInputToggle';
LocalizedInputToggle.propTypes = {
icon: PropTypes.node,
isOpen: PropTypes.bool,
onClick: PropTypes.func.isRequired,
isDisabled: PropTypes.bool,
showMessage: PropTypes.oneOfType([PropTypes.string, intlMessageShape])
.isRequired,
hideMessage: PropTypes.oneOfType([PropTypes.string, intlMessageShape])
.isRequired,
remainingLocalizations: PropTypes.number,
};
LocalizedInputToggle.defaultProps = {
hideMessage: messages.hide,
showMessage: messages.show,
};

export default LocalizedInputToggle;

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import type { Theme } from '@emotion/react';
import { css } from '@emotion/react';
import { customProperties as vars } from '@commercetools-uikit/design-system';
import { getInputStyles } from '../styles';
import type { TMultiLineInputProps } from './multiline-input';

/* we need this line-height to achieve 32px height when the component has only one row */
const sizeInputLineHeight = '22px';
Expand All @@ -9,9 +11,9 @@ const sizeInputLineHeight = '22px';
// * a disabled-field currently does not display warning/error-states so it takes precedence
// * a readonly-field cannot be changed, but it might be relevant for validation, so error and warning are checked first
// how you can interact with the field is controlled separately by the props, this only influences visuals
const getTextareaStyles = (props) => {
const getTextareaStyles = (props: TMultiLineInputProps, theme: Theme) => {
const baseStyles = [
getInputStyles(props, props.theme),
getInputStyles(props, theme),
css`
padding: ${vars.spacingXs} ${vars.spacingS};
line-height: ${sizeInputLineHeight};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import React, { ChangeEventHandler } from 'react';
import { useTheme } from '@emotion/react';
import TextareaAutosize, {
TextareaHeightChangeMeta,
} from 'react-textarea-autosize';
import { filterDataAttributes, warning } from '@commercetools-uikit/utils';
import { getTextareaStyles } from './multiline-input.styles';

const MIN_ROW_COUNT = 1;

export type TMultiLineInputProps = {
autoComplete?: string;
className?: string;
hasError?: boolean;
hasWarning?: boolean;
id?: string;
isAutofocussed?: boolean;
isDisabled?: boolean;
isReadOnly?: boolean;
name?: string;
onBlur?: ChangeEventHandler;
onChange?: ChangeEventHandler;
onFocus?: ChangeEventHandler;
placeholder?: string;
value: string;
isOpen: boolean;
onHeightChange?: (height: number, rowCount: number) => void;
};

const MultilineInput = (props: TMultiLineInputProps) => {
const theme = useTheme();
const { onHeightChange } = props;
const ref = React.useRef<HTMLTextAreaElement | null>(null);
const handleHeightChange = React.useCallback<
(height: number, meta: TextareaHeightChangeMeta) => void
>(
(height: number, meta: TextareaHeightChangeMeta) => {
const rowCount = Math.floor(
ref.current?.scrollHeight || 0 / meta.rowHeight
);
if (onHeightChange) {
onHeightChange(height, rowCount);
}
},
[ref, onHeightChange]
);

if (!props.isReadOnly) {
warning(
typeof props.onChange === 'function',
'MultilineInput: "onChange" is required when is not read only.'
);
}

return (
<TextareaAutosize
ref={ref}
name={props.name}
onHeightChange={handleHeightChange}
autoComplete={props.autoComplete}
value={props.value}
onChange={props.onChange}
id={props.id}
onBlur={props.onBlur}
onFocus={props.onFocus}
disabled={props.isDisabled}
placeholder={props.placeholder}
readOnly={props.isReadOnly}
autoFocus={props.isAutofocussed}
css={getTextareaStyles(props, theme)}
// Allow to override the styles by passing a `className` prop.
// Custom styles can also be passed using the `css` prop from emotion.
// https://emotion.sh/docs/css-prop#style-precedence
className={props.className}
/* ARIA */
aria-readonly={props.isReadOnly}
aria-multiline="true"
role="textbox"
minRows={MIN_ROW_COUNT}
maxRows={props.isOpen ? undefined : MIN_ROW_COUNT}
cacheMeasurements={true}
{...filterDataAttributes(props)}
/>
);
};

MultilineInput.displayName = 'MultilineInput';

export default MultilineInput;
Original file line number Diff line number Diff line change
@@ -1,10 +1,24 @@
import type { Theme } from '@emotion/react';
import { css } from '@emotion/react';
import {
customProperties,
designTokens,
} from '@commercetools-uikit/design-system';

const getInputBorderColor = (vars, props) => {
type TExtendedTheme = {
[key: string]: string;
} & Theme;

type TInputProps = {
isDisabled?: boolean;
disabled?: boolean;
hasError?: boolean;
hasWarning?: boolean;
isReadOnly?: boolean;
readOnly?: boolean;
};

const getInputBorderColor = (vars: TExtendedTheme, props: TInputProps) => {
if (props.isDisabled || props.disabled) {
return vars[designTokens.borderColorForInputWhenDisabled];
}
Expand All @@ -19,7 +33,8 @@ const getInputBorderColor = (vars, props) => {
}
return vars[designTokens.borderColorForInput];
};
const getInputFontColor = (vars, props) => {

const getInputFontColor = (vars: TExtendedTheme, props: TInputProps) => {
if (props.isDisabled || props.disabled) {
return vars[designTokens.fontColorForInputWhenDisabled];
}
Expand All @@ -34,8 +49,9 @@ const getInputFontColor = (vars, props) => {
}
return vars[designTokens.fontColorForInput];
};
const getInputStyles = (props, theme) => {
const vars = {

const getInputStyles = (props: TInputProps, theme: Theme) => {
const vars: TExtendedTheme = {
...customProperties,
...theme,
};
Expand Down

1 comment on commit 4acb66d

@vercel
Copy link

@vercel vercel bot commented on 4acb66d Apr 27, 2021

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.