8000 Feat(core): InputProps should inherit from built-in ones by thien-do · Pull Request #239 · thien-do/moai · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Feat(core): InputProps should inherit from built-in ones #239

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 29, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
167 changes: 19 additions & 148 deletions lib/core/src/input/input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { border } from "../border/border";
import { Icon, IconComponent } from "../icon/icon";
import { outline } from "../outline/outline";
import { text } from "../text/text";
import { omit } from "../utils/omit";
import sFlat from "./flat.module.css";
import s from "./input.module.css";
import sOutset from "./outset.module.css";
Expand All @@ -19,33 +20,29 @@ export interface InputSize {
mainColor: string;
}

export interface InputProps {
/**
* The [HTML `type`][1] attribute, such as "email", "password" or "date".
*
* [1]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#input_types
*/
type?: string;
type HTMLInputProps = React.InputHTMLAttributes<HTMLInputElement>;

// Uncontrolled
export interface InputProps
extends Omit<HTMLInputProps, "size" | "list" | "style"> {
// We intentionally re-define some props here even though they exist in
// HTMLInputProps so that we can have documentation for them

/**
* The HTML type attribute, such as "email", "password" or "date".
*/
type?: HTMLInputProps["type"];
/**
* Initial value of the input in uncontrolled mode.
*/
defaultValue?: string | number;

// Controlled

defaultValue?: HTMLInputProps["defaultValue"];
/**
* Value of the input in controlled mode.
*/
value?: string;

/**
* Callback to set the value in controlled mode.
*/
setValue?: (value: string) => void;

/**
* Id of an [HTML `datalist`][1] element to be used with the Input. Can
* also pass an object to let the Input component create the `datalist`
Expand All @@ -54,9 +51,6 @@ export interface InputProps {
* [1]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/datalist
*/
list?: { id: string; values: string[] } | string;

// Style

/**
* Icon in the input. See the [Icons guide][1] to learn more.
*
Expand All @@ -71,120 +65,14 @@ export interface InputProps {
* Size of the text box. Choose one from `Input.sizes`.
*/
size?: InputSize;

// Attributes

/**
* The [HTML `id`][1] attribute of the Input
*
* [1]: https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/id
*/
id?: string;
/**
* The [HTML `name`][1] attribute of the Input, usually used in forms
*
* [1]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#htmlattrdefname
*/
name?: string;
/**
* The [HTML `disabled`][1] attribute. If true, users cannot select or
* change the text. See also: "readOnly".
*
* [1]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-disabled
*/
disabled?: boolean;
/**
* The [HTML `disabled`][1] attribute. If true, users can select text
* inside the text box but cannot change it. This is only for inputs that
* are rendered as text boxes.
*
* [1]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-readonly
*/
readOnly?: boolean;
/**
* The [HTML `placeholder`][1] attribute. A string to display inside the
* text box when it is empty.
*
* [1]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-placeholder
*/
placeholder?: string;
/**
* The [HTML `autoFocus`][1] attribute. If true, the input will have focus
* on its initial render.
*
* [1]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-autofocus
*/
autoFocus?: boolean;
/**
* A text to label the input. Should use when there is no linked HTML
* `label` element.
*/
"aria-label"?: string;
/**
* Id of the element that labels the input. Useful when the labels don't
* use the HTML `label` element.
*/
"aria-labelledby"?: string;
/**
* The maximum number of characters of the text.
*/
maxLength?: number;
/**
* The [HTML `required`][1] attribute. If true, a value is required before
* form submission. This is based on HTML5's validation.
*
* [1]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-required
*/
required?: boolean;

// Events

/**
* The [HTML `onblur`][1] event handler.
*
* [1]: https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onblur
*/
onBlur?: React.FocusEventHandler<HTMLInputElement>;
/**
* The [HTML `onfocus`][1] event handler.
* DO NOT USE THIS.
*
* [1]: https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onfocus
* onChange is the raw prop, exists only for compatibility with 3rd-party
* libraries (those that passing props to a custom component). For direct
* usage, use `setValue`.
*/
onFocus?: React.FocusEventHandler<HTMLInputElement>;
/**
* The [HTML `onkeypress`][1] event handler.
*
* [1]: https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onkeypress
*/
onKeyPress?: React.KeyboardEventHandler<HTMLInputElement>;
/**
* The [HTML `onkeyup`][1] event handler.
*
* [1]: https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onkeyup
*/
onKeyUp?: React.KeyboardEventHandler<HTMLInputElement>;
/**
* The [HTML `onkeydown`][1] event handler.
*
* [1]: https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onkeydown
*/
onKeyDown?: React.KeyboardEventHandler<HTMLInputElement>;
/**
* The [HTML `onclick`][1] event handler.
*
* [1]: https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onclick
*/
onClick?: React.KeyboardEventHandler<HTMLInputElement>;
/**
* The [HTML `onchange`][1] event handler.
*
* Note that you should not need to use onChange! This exists only for
* compatibility with 3rd-party libraries (those that passing props to a
* custom rendered component). You should use `setValue` most of the time.
*
* [1]: https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onchange
*/
onChange?: React.ChangeEventHandler<HTMLInputElement>;
onChange?: HTMLInputProps["onChange"];
}

// This is actually ReturnType<typeof forwardRef>, but we don't know how to
Expand Down Expand Up @@ -235,41 +123,24 @@ const inputRender = (
): JSX.Element => {
validate(props);
const size = props.size ?? Input.sizes.medium;
const rawProps = omit(props, ["size", "style"]);

return (
<div className={s.container}>
<input
{...rawProps}
ref={ref}
// Value
type={props.type}
defaultValue={props.defaultValue}
value={props.value}
=> {
props.onChange?.(event);
props.setValue?.(event.currentTarget.value);
}}
// Event handlers
>
>
>
>
>
// Properties
id={props.id}
name={props.name}
className={getClass(props)}
list={
typeof props.list === "string"
? props.list
: props.list?.id ?? undefined
}
readOnly={props.readOnly}
disabled={props.disabled}
placeholder={props.placeholder}
autoFocus={props.autoFocus}
aria-label={props["aria-label"]}
aria-labelledby={props["aria-labelledby"]}
maxLength={props.maxLength}
required={props.required}
/>
{props.icon && (
<div className={[s.icon, text.muted, size.icon].join(" ")}>
Expand Down
22 changes: 22 additions & 0 deletions lib/core/src/utils/omit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// https://stackoverflow.com/questions/53966509/typescript-type-safe-omit-function

type Keys<T> = (keyof T)[];

interface OmitFunction {
<T, K extends Keys<T>>(obj: T, keys: K): {
[K2 in Exclude<keyof T, K[number]>]: T[K2];
};
}

export const omit: OmitFunction = (obj, keys) => {
const ret = {} as {
[K in keyof typeof obj]: typeof obj[K];
};
let key: keyof typeof obj;
for (key in obj) {
if (!keys.includes(key)) {
ret[key] = obj[key];
}
}
return ret;
};
35 changes: 6 additions & 29 deletions lib/docs/src/components/input.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,38 +11,15 @@ const meta: Meta = {
title: "Components/Input",
component: Input,
argTypes: {
type: Utils.arg(
["text", "number", "email", "password", "url", "color"],
"Visual"
),
type: Utils.arg("string", "Visual"),
style: Utils.arg(Input.styles, "Visual"),
size: Utils.arg(Input.sizes, "Visual"),
placeholder: Utils.arg("string", "Visual"),
icon: Utils.arg(null, "Visual"),

defaultValue: Utils.arg(null, "Uncontrolled"),

value: Utils.arg(null, "Controlled"),
setValue: Utils.arg(null, "Controlled"),

id: Utils.arg(null, "Attributes"),
name: Utils.arg("string", "Attributes"),
list: Utils.arg(null, "Attributes"),
maxLength: Utils.arg("number", "Attributes"),
disabled: Utils.arg("boolean", "Attributes"),
required: Utils.arg("boolean", "Attributes"),
readOnly: Utils.arg("boolean", "Attributes"),
autoFocus: Utils.arg(null, "Attributes"),
"aria-label": Utils.arg(null, "Attributes"),
"aria-labelledby": Utils.arg(null, "Attributes"),

onBlur: Utils.arg(null, "Events"),
onFocus: Utils.arg(null, "Events"),
onKeyPress: Utils.arg(null, "Events"),
onKeyUp: Utils.arg(null, "Events"),
onKeyDown: Utils.arg(null, "Events"),
onClick: Utils.arg(null, "Events"),
onChange: Utils.arg(null, "Events"),
list: Utils.arg(null, "Visual"),
defaultValue: Utils.arg(null, "State"),
value: Utils.arg(null, "State"),
setValue: Utils.arg(null, "State"),
onChange: Utils.arg(null, "State"),
},
};

Expand Down
0