Skip to content

Commit

Permalink
Merge pull request #107 from aversini/feat-Adding-TextInputMask
Browse files Browse the repository at this point in the history
feat: adding TextInputMask component
  • Loading branch information
aversini authored Dec 2, 2023
2 parents fa5ba15 + 8baff39 commit 4a6fd2b
Show file tree
Hide file tree
Showing 11 changed files with 896 additions and 87 deletions.
73 changes: 73 additions & 0 deletions packages/documentation/src/stories/TextInputMask.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import type { Meta, StoryObj } from "@storybook/react";
import { TextInputMask } from "@versini/ui-components";

const meta: Meta<typeof TextInputMask> = {
component: TextInputMask,
parameters: {
controls: { exclude: ["spacing", "rightElement"], sort: "requiredFirst" },
},
args: {
label: "Enter password",
name: "password",
disabled: false,
helperText: "",
raw: false,
focusKind: "light",
borderKind: "dark",
errorKind: "light",
error: false,
inputClassName: "",
className: "",
},
argTypes: {
className: {
control: "text",
},
inputClassName: {
control: "text",
},
labelId: {
control: "text",
},
id: {
control: "text",
},
helperText: {
control: "text",
},
raw: {
control: "boolean",
},
error: {
control: "boolean",
},
focusKind: {
options: ["dark", "light"],
control: { type: "radio" },
},
borderKind: {
options: ["dark", "light"],
control: { type: "radio" },
},
errorKind: {
options: ["dark", "light"],
control: { type: "radio" },
},
},
};

export default meta;

type Story = StoryObj<typeof TextInputMask>;

export const Basic: Story = {
render: (args) => (
<div className="min-h-10 bg-slate-500 p-11">
<form noValidate>
<div className="flex gap-2">
<TextInputMask {...args} />
</div>
</form>
</div>
),
};
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { render } from "@testing-library/react";
import * as React from "react";
import { describe, expect, it } from "vitest";

import { getSpacing, isEmpty, truncate } from "../utilities";
import { getSpacing, isEmpty, mergeRefs, truncate } from "../utilities";

describe("Non-DOM tests", () => {
describe("getSpacing", () => {
Expand Down Expand Up @@ -67,3 +69,30 @@ describe("Non-DOM tests", () => {
});
});
});

describe("DOM tests", () => {
describe("when testing for mergeRefs", () => {
it("should combine multiple refs of different types", () => {
const Dummy = React.forwardRef(function Dummy(_, ref) {
React.useImperativeHandle(ref, () => "refValue");
return null;
});
const refAsFunc = vi.fn();
const refAsObj = { current: undefined };
const Example: React.FC<{ visible: boolean }> = ({ visible }) => {
return visible ? (
<Dummy ref={mergeRefs([refAsObj, refAsFunc])} />
) : null;
};
const { rerender } = render(<Example visible />);
expect(refAsFunc).toHaveBeenCalledTimes(1);
expect(refAsFunc).toHaveBeenCalledWith("refValue");
expect(refAsObj.current).toBe("refValue");

rerender(<Example visible={false} />);
expect(refAsFunc).toHaveBeenCalledTimes(2);
expect(refAsFunc).toHaveBeenCalledWith(null);
expect(refAsObj.current).toBe(null);
});
});
});
31 changes: 31 additions & 0 deletions packages/ui-components/src/common/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,3 +112,34 @@ export const getSpacing = (spacing: SpacingType): string => {

return spacingClass;
};

/**
* React utility to merge refs.
* When developing low level UI components, it is common to have to use a local
* ref but also support an external one using React.forwardRef. Natively, React
* does not offer a way to set two refs inside the ref property.
* @param Array of refs (object, function, etc.)
*
* @example
*
* const Example = React.forwardRef(function Example(props, ref) {
* const localRef = React.useRef();
* return <div ref={mergeRefs([localRef, ref])} />;
* });
*
*/
export function mergeRefs<T = any>(
refs: Array<
React.MutableRefObject<T> | React.LegacyRef<T> | undefined | null
>,
): React.RefCallback<T> {
return (value) => {
refs.forEach((ref) => {
if (typeof ref === "function") {
ref(value);
} else if (ref != null) {
(ref as React.MutableRefObject<T | null>).current = value;
}
});
};
}
3 changes: 3 additions & 0 deletions packages/ui-components/src/components/Button/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
raw = false,
noBorder = false,
"aria-label": ariaLabel,

...otherProps
},
ref,
) => {
Expand All @@ -43,6 +45,7 @@ export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
disabled={disabled}
type={type}
aria-label={ariaLabel}
{...otherProps}
>
{children}
</button>
Expand Down
3 changes: 3 additions & 0 deletions packages/ui-components/src/components/Button/ButtonIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ export const ButtonIcon = React.forwardRef<HTMLButtonElement, ButtonIconProps>(
"aria-label": ariaLabel,
label,
size = "medium",

...otherProps
},
ref,
) => {
Expand All @@ -43,6 +45,7 @@ export const ButtonIcon = React.forwardRef<HTMLButtonElement, ButtonIconProps>(
disabled={disabled}
type={type}
aria-label={ariaLabel || label}
{...otherProps}
>
{children}
</button>
Expand Down
3 changes: 3 additions & 0 deletions packages/ui-components/src/components/Button/ButtonLink.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ export const ButtonLink = React.forwardRef<HTMLAnchorElement, ButtonLinkProps>(
link,
target,
maxLabelLength,

...otherProps
},
ref,
) => {
Expand All @@ -44,6 +46,7 @@ export const ButtonLink = React.forwardRef<HTMLAnchorElement, ButtonLinkProps>(
const extraProps = {
target,
rel: target === "_blank" ? "noopener noreferrer" : undefined,
...otherProps,
};

return (
Expand Down
Loading

0 comments on commit 4a6fd2b

Please sign in to comment.