Skip to content

Commit

Permalink
#65 Improve NumberInput, useIntlContext and docs
Browse files Browse the repository at this point in the history
  • Loading branch information
Felix Beceic committed Apr 26, 2023
1 parent ee65872 commit 3902cf8
Show file tree
Hide file tree
Showing 18 changed files with 178 additions and 64 deletions.
14 changes: 13 additions & 1 deletion libs/core/src/Pagination.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,19 @@ import ButtonGroups from "./ButtonGroups";

export type PaginationProps = {
/**
* Complex values of types PageInfo and Components for formatting purposes.
* Enables you to handle the page summary text next to the pagination by using the provided values
* of types:
* - **PageInfo** (number values _from_, _to_ and _totalElements_) and/or
* - **Components** (React.ReactNode values _from_, _to_ and _totalElements_ with 'font-medium' class name applied).
*
* @example
* <Pagination {...paginationState} {...paginationHook}>
* {(_pageInfo, components) => (
* <span>
* {components.from}-{components.to} from {components.totalElements}
* </span>
* )}
* </Pagination>
*/
children?: (pageInfo: PageInfo, components: Components) => React.ReactNode;

Expand Down
4 changes: 2 additions & 2 deletions libs/date/src/DateInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ export default function DateInput({
}: DateInputProps) {
const inputRef = React.useRef<HTMLInputElement>(null);
const datePickerRef = React.useRef<HTMLDivElement>(null);
const lang = useIntlContext(true)?.lang;
const lang = useIntlContext()?.lang || "en";

const finalDateFormat = dateFormat?.replace(/m/g, "M") || getDateFormatByLang(lang);
const formattedValue = value ? dateFns.format(value, finalDateFormat) : "";
Expand Down Expand Up @@ -314,7 +314,7 @@ function DateInputInput({
dateFormat,
...props
}: DateInputInputProps) {
const lang = useIntlContext(true)?.lang;
const lang = useIntlContext()?.lang || "en";

const tokens = useTokens("DateInput", props.tokens);

Expand Down
4 changes: 2 additions & 2 deletions libs/date/src/DateRangeInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ export default function DateRangeInput({
highlightToday,
...props
}: DateRangeInputProps) {
const lang = useIntlContext(true)?.lang;
const lang = useIntlContext()?.lang || "en";

const finalDateFormat = dateFormat?.replace(/m/g, "M") || getDateFormatByLang(lang);
const formattedStart = start ? dateFns.format(start, finalDateFormat) : "";
Expand Down Expand Up @@ -427,7 +427,7 @@ function DateRangeInputInput({
dateFormat,
...props
}: DateRangeInputInputProps) {
const lang = useIntlContext(true)?.lang;
const lang = useIntlContext()?.lang || "en";

const tokens = useTokens("DateInput", props.tokens);
const dateIconClassName = cx({ [tokens.DatePicker.range.iconColor]: !(props.disabled || props.readOnly) });
Expand Down
2 changes: 1 addition & 1 deletion libs/date/src/DateTimeInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ export default function DateTimeInput({
highlightToday,
...props
}: DateTimeInputProps & DateTimeInputTokens) {
const lang = useIntlContext(true)?.lang;
const lang = useIntlContext()?.lang || "en";

const dateTimePickerTokens = useTokens("DateTimePicker", props.dateTimePickerTokens);
const dateTimeInputTokens = useTokens("DateTimeInput", props.dateTimeInputTokens);
Expand Down
94 changes: 67 additions & 27 deletions libs/form-elements/src/NumberInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,13 @@ export type NumberInputProps = {
*/
className?: string;

/**
* Custom decimal separator (e.g. "," or ".").
*
* If not provided, it will be inferred from the IntlProvider.
*/
decimalSeparator?: string;

/**
* Determines whether the component is disabled.
*/
Expand Down Expand Up @@ -85,41 +92,74 @@ export type NumberInputProps = {
*/
required?: boolean;

/**
* Custom thousand separator (e.g. "." or ",").
*
* If not provided, it will be inferred from the IntlProvider.
*/
thousandSeparator?: string;

/**
* The value of the field sent on submit and/or retrieved on component render.
*/
value?: string;
} & Omit<NumberFormatProps, NumberFormatOnlyPropsUnion>;

export default function NumberInput({ name, onChange, onBlur, ...props }: NumberInputProps) {
export default function NumberInput({
name,
onChange,
onBlur,
decimalSeparator,
thousandSeparator,
...props
}: NumberInputProps) {
const intlContext = useIntlContext();
const id = `numberformat-${name}`;

if (intlContext) {
const { intl } = intlContext;
const decimalSeparator = getDecimalSeparator(intl);
const thousandSeparator = getThousandSeparator(intl);

const id = `numberformat-${name}`;

return (
<ReactNumberFormat
id={id}
data-testid={id}
name={name}
decimalSeparator={decimalSeparator}
thousandSeparator={thousandSeparator}
allowedDecimalSeparators={[decimalSeparator]}
onValueChange={(values) => {
if (onChange) {
onChange(values.floatValue);
}
}}
customInput={Input}
onBlur={onBlur}
{...props}
/>
if (!intlContext && (!decimalSeparator || !thousandSeparator)) {
throw new Error(
"You must pass decimalSeparator and thousandSeparator props if you are using the NumberInput component without IntlProvider.",
);
} else {
throw new Error("NumberInput component requires IntlProvider wrapper for functioning.");
}
const getFinalDecimalSeparator = () => {
if (decimalSeparator && thousandSeparator) {
return decimalSeparator;
}
if (intlContext) {
const { intl } = intlContext;
return getDecimalSeparator(intl);
}
};

const getFinalThousandSeparator = () => {
if (decimalSeparator && thousandSeparator) {
return thousandSeparator;
}
if (intlContext) {
const { intl } = intlContext;
return getThousandSeparator(intl);
}
};

const finalDecimalSeparator = getFinalDecimalSeparator() as string;
const finalThousandSeparator = getFinalThousandSeparator() as string;

return (
<ReactNumberFormat
id={id}
data-testid={id}
name={name}
decimalSeparator={finalDecimalSeparator}
thousandSeparator={finalThousandSeparator}
allowedDecimalSeparators={[finalDecimalSeparator]}
onValueChange={(values) => {
if (onChange) {
onChange(values.floatValue);
}
}}
customInput={Input}
onBlur={onBlur}
{...props}
/>
);
}
3 changes: 2 additions & 1 deletion libs/intl/src/Intl.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,5 +56,6 @@ export default function Intl({ name, params, children }: IntlProps) {
</>
);
}
return <></>;

throw new Error("You can't use the Intl component unless it's wrapped with IntlProvider.");
}
10 changes: 3 additions & 7 deletions libs/intl/src/useIntlContext.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
import * as React from "react";

import { IntlContext, IntlContextType } from "./IntlProvider";
import { IntlContext } from "./IntlProvider";

export function useIntlContext(returnDefault?: boolean) {
export function useIntlContext() {
const context = React.useContext(IntlContext);

if (!context && !returnDefault) {
if (!context) {
return undefined;
}

if (!context && returnDefault) {
return { lang: "en" } as IntlContextType;
}

return context;
}
5 changes: 2 additions & 3 deletions storybook/src/core/Pagination.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,9 @@ export const Custom = () => {
</div>
<div className="flex mt-4 w-fit">
<Pagination {...paginationState} {...paginationHook}>
{(pageInfo) => (
{(_pageInfo, components) => (
<span>
<span className="font-medium">{pageInfo.from}</span>-<span className="font-medium">{pageInfo.to}</span>{" "}
from <span className="font-medium">{pageInfo.totalElements}</span>
{components.from}-{components.to} from {components.totalElements}
</span>
)}
</Pagination>
Expand Down
13 changes: 10 additions & 3 deletions storybook/src/date/DateInput.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,6 @@ The component accepts the following types for **value** prop:
- null
- Date type - example: _new Date()_, example 2: _new Date("2019-01-20")_

**Note**: if using the component without the `IntlProvider` wrapper the date format defaults to _en_ if not specified,
otherwise the format is determined by the `dateFormat` prop.

For integration with Formik see docs [here](/docs/component-library-formik-elements-dateinputfield--with-label#dateinputfield).

Props are described on the [Date Input Props section](/docs/component-library-date-dateinput--disabled#date-input-props)
Expand All @@ -26,6 +23,16 @@ of the documentation.

- Intl Provider (details [here](/docs/component-library-intl-intl--basic))

## Usage alongside IntlProvider

When rendering this component with `IntlProvider` as its wrapper in the component tree, the format will be
automatically inferred from the `locale` prop of `IntlProvider`.

## Usage without IntlProvider

If using the component without the `IntlProvider` wrapper the date format defaults to _en_ if not specified,
otherwise the format is determined by the `dateFormat` prop.

<Stories includePrimary={true} />

## Date Input Props:
Expand Down
13 changes: 10 additions & 3 deletions storybook/src/date/DateRangeInput.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,6 @@ The component accepts the following as initial values for **start** and **end**
- undefined
- Date type - example: _new Date()_, example 2: _new Date("2023-01-20")_

**Note**: if using the component without the `IntlProvider` wrapper the date format defaults to _en_ if not specified,
otherwise the format is determined by the `dateFormat` prop.

For integration with Formik see docs [here](/docs/component-library-formik-elements-daterangeinputfield--with-label#daterangeinputfield).

Props are described on the [Date Range Input Props section](/docs/component-library-date-daterangeinput--disabled#date-range-input-props)
Expand All @@ -24,6 +21,16 @@ of the documentation.

- Intl Provider (details [here](/docs/component-library-intl-intl--basic))

## Usage alongside IntlProvider

When rendering this component with `IntlProvider` as its wrapper in the component tree, the format will be
automatically inferred from the `locale` prop of `IntlProvider`.

## Usage without IntlProvider

If using the component without the `IntlProvider` wrapper the date format defaults to _en_ if not specified,
otherwise the format is determined by the `dateFormat` prop.

<Stories includePrimary={true} />

## Date Range Input Props:
Expand Down
13 changes: 10 additions & 3 deletions storybook/src/date/DateTimeInput.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,6 @@ The component accepts the following types for **value** prop:

There are 4 steps available (year, date, hour and minute), so tabs are required to visually distinguish date/time steps.

**Note**: if using the component without the `IntlProvider` wrapper the date format defaults to _en_ if not specified,
otherwise the format is determined by the `dateFormat` prop.

For integration with Formik see docs [here](/docs/component-library-formik-elements-datetimeinputfield--with-label#datetimeinputfield).

Props are described on the [Date Time Input Props section](/docs/component-library-date-datetimeinput--disabled#date-time-input-props)
Expand All @@ -26,6 +23,16 @@ of the documentation.

- Intl Provider (details [here](/docs/component-library-intl-intl--basic))

## Usage alongside IntlProvider

When rendering this component with `IntlProvider` as its wrapper in the component tree, the format will be
automatically inferred from the `locale` prop of `IntlProvider`.

## Usage without IntlProvider

If using the component without the `IntlProvider` wrapper the date format defaults to _en_ if not specified,
otherwise the format is determined by the `dateFormat` prop.

<Stories includePrimary={true} />

## Date Time Input Props:
Expand Down
12 changes: 11 additions & 1 deletion storybook/src/form-elements/NumberInput.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,20 @@ Do not use it for other digit-based values, such as telephone, credit card, and

When applicable, set the most common choice as the default value. For example, airline, bus, train and other travel companies typically default the number of passengers to 1.

## **Prerequisites for functioning**:
## **Recommended for optimal usage**:

- Intl Provider (details [here](/docs/component-library-intl-intl--basic))

## Usage alongside IntlProvider

When rendering this component with `IntlProvider` as its wrapper in the component tree, the format will be
automatically inferred from the `locale` prop of `IntlProvider`.

## Usage without IntlProvider

If you wish to use this component without `IntlProvider`, you can do so by passing `thousandSeparator` and `decimalSeparator` props
to the component. This way, the format will be inferred from these props instead of the `locale` prop of `IntlProvider`.

<Stories includePrimary={true} />

## Number Input Props:
Expand Down
2 changes: 2 additions & 0 deletions storybook/src/form-elements/NumberInput.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -125,3 +125,5 @@ export const WithInlineLeadingAndTrailingAddOn = () => (
inlineTrailingAddOn={inlineTrailingAddOn}
/>
);

export const WithoutIntlProvider = () => <NumberInput name={name} decimalSeparator="," thousandSeparator="." />;
13 changes: 10 additions & 3 deletions storybook/src/formik-elements/DateInputField.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,6 @@ The component accepts the following types as initial values for **name** accesso
- Date type - example: _new Date()_, example 2: _new Date("2023-01-20")_
- string type in format 'yyyy-mm-dd' - example: _"2024-01-01"_, example 2: _"2023-06-21"_

**Note**: if using the component without the `IntlProvider` wrapper the date format defaults to _en_ if not specified,
otherwise the format is determined by the `dateFormat` prop.

This is a **field** component, which means it is differs from [Date Input](/docs/component-library-date-dateinput--with-state#dateinput) by having an integrated logic for `onChange`, `onBlur` and `onReset`
functions which `Date Input` possesses using Formik. There is no need for any custom logic, just wrap the component inside Formik for easier form creation.

Expand All @@ -30,6 +27,16 @@ of the documentation.

- Intl Provider (details [here](/docs/component-library-intl-intl--basic))

## Usage alongside IntlProvider

When rendering this component with `IntlProvider` as its wrapper in the component tree, the format will be
automatically inferred from the `locale` prop of `IntlProvider`.

## Usage without IntlProvider

If using the component without the `IntlProvider` wrapper the date format defaults to _en_ if not specified,
otherwise the format is determined by the `dateFormat` prop.

<Stories includePrimary={true} />

## Date Input Field Props:
Expand Down
13 changes: 10 additions & 3 deletions storybook/src/formik-elements/DateRangeInputField.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,6 @@ The component accepts the following as initial values for **start** and **end**

If a string value is given, the component itself makes sure to convert the string into a Date format.

**Note**: if using the component without the `IntlProvider` wrapper the date format defaults to _en_ if not specified,
otherwise the format is determined by the `dateFormat` prop.

This is a **field** component, which means it is differs from [Date Range Input](/docs/component-library-date-daterangeinput--with-state#date-range-input)
by having a provided logic for seamless wrapping of the component inside Formik for easier form creation.

Expand All @@ -29,6 +26,16 @@ of the documentation.

- Intl Provider (details [here](/docs/component-library-intl-intl--basic))

## Usage alongside IntlProvider

When rendering this component with `IntlProvider` as its wrapper in the component tree, the format will be
automatically inferred from the `locale` prop of `IntlProvider`.

## Usage without IntlProvider

If using the component without the `IntlProvider` wrapper the date format defaults to _en_ if not specified,
otherwise the format is determined by the `dateFormat` prop.

<Stories includePrimary={true} />

## Date Range Input Props:
Expand Down
Loading

0 comments on commit 3902cf8

Please sign in to comment.