From d0227d433b8f45f4edc55847ace0e83ec47803b6 Mon Sep 17 00:00:00 2001 From: Thien Do Date: Tue, 29 Jun 2021 03:14:30 +0700 Subject: [PATCH 1/3] Feat(core): Support autoComplete in Input Fix #159 --- lib/core/src/input/input.tsx | 10 ++++++++++ lib/docs/src/components/input.stories.tsx | 1 + 2 files changed, 11 insertions(+) diff --git a/lib/core/src/input/input.tsx b/lib/core/src/input/input.tsx index e0b1ce84..c4fccf6c 100644 --- a/lib/core/src/input/input.tsx +++ b/lib/core/src/input/input.tsx @@ -19,6 +19,8 @@ export interface InputSize { mainColor: string; } +type HTMLInput = React.InputHTMLAttributes; + export interface InputProps { /** * The [HTML `type`][1] attribute, such as "email", "password" or "date". @@ -115,6 +117,13 @@ export interface InputProps { * [1]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-autofocus */ autoFocus?: boolean; + /** + * The [HTML `autoComplete`][1] attribute. If true, the input will have focus + * on its initial render. + * + * [1]: https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete + */ + autoComplete?: HTMLInput["autoComplete"]; /** * A text to label the input. Should use when there is no linked HTML * `label` element. @@ -266,6 +275,7 @@ const inputRender = ( disabled={props.disabled} placeholder={props.placeholder} autoFocus={props.autoFocus} + autoComplete={props.autoComplete} aria-label={props["aria-label"]} aria-labelledby={props["aria-labelledby"]} maxLength={props.maxLength} diff --git a/lib/docs/src/components/input.stories.tsx b/lib/docs/src/components/input.stories.tsx index 735008be..6997e031 100644 --- a/lib/docs/src/components/input.stories.tsx +++ b/lib/docs/src/components/input.stories.tsx @@ -33,6 +33,7 @@ const meta: Meta = { required: Utils.arg("boolean", "Attributes"), readOnly: Utils.arg("boolean", "Attributes"), autoFocus: Utils.arg(null, "Attributes"), + autoComplete: Utils.arg("string", "Attributes"), "aria-label": Utils.arg(null, "Attributes"), "aria-labelledby": Utils.arg(null, "Attributes"), From 7cbce431ed2827c493e65e1eb58c50a0c22bcb31 Mon Sep 17 00:00:00 2001 From: Thien Do Date: Tue, 29 Jun 2021 04:20:19 +0700 Subject: [PATCH 2/3] Refactor(core): InputProps should inherit from built-in ones Fix #160 --- lib/core/src/input/input.tsx | 175 +++------------------- lib/core/src/utils/omit.ts | 22 +++ lib/docs/src/components/input.stories.tsx | 36 +---- 3 files changed, 46 insertions(+), 187 deletions(-) create mode 100644 lib/core/src/utils/omit.ts diff --git a/lib/core/src/input/input.tsx b/lib/core/src/input/input.tsx index c4fccf6c..9d3bb4f5 100644 --- a/lib/core/src/input/input.tsx +++ b/lib/core/src/input/input.tsx @@ -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"; @@ -19,35 +20,29 @@ export interface InputSize { mainColor: string; } -type HTMLInput = React.InputHTMLAttributes; +type HTMLInputProps = React.InputHTMLAttributes; + +export interface InputProps + extends Omit { + // We intentionally re-define some props here even though they exist in + // HTMLInputProps so that we can have documentation for them -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 + * The HTML type attribute, such as "email", "password" or "date". */ - type?: string; - - // Uncontrolled - + 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` @@ -56,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. * @@ -73,127 +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; - /** - * The [HTML `autoComplete`][1] attribute. If true, the input will have focus - * on its initial render. - * - * [1]: https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete - */ - autoComplete?: HTMLInput["autoComplete"]; - /** - * 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; - /** - * The [HTML `onfocus`][1] event handler. - * - * [1]: https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onfocus - */ - onFocus?: React.FocusEventHandler; - /** - * The [HTML `onkeypress`][1] event handler. - * - * [1]: https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onkeypress - */ - onKeyPress?: React.KeyboardEventHandler; - /** - * The [HTML `onkeyup`][1] event handler. - * - * [1]: https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onkeyup - */ - onKeyUp?: React.KeyboardEventHandler; /** - * The [HTML `onkeydown`][1] event handler. + * DO NOT USE THIS. * - * [1]: https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onkeydown + * 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`. */ - onKeyDown?: React.KeyboardEventHandler; - /** - * The [HTML `onclick`][1] event handler. - * - * [1]: https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onclick - */ - onClick?: React.KeyboardEventHandler; - /** - * 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; + onChange?: HTMLInputProps["onChange"]; } // This is actually ReturnType, but we don't know how to @@ -244,42 +123,24 @@ const inputRender = ( ): JSX.Element => { validate(props); const size = props.size ?? Input.sizes.medium; + const rawProps = omit(props, ["size", "style"]); + return (
{ props.onChange?.(event); props.setValue?.(event.currentTarget.value); }} - // Event handlers - onBlur={props.onBlur} - onFocus={props.onFocus} - onKeyDown={props.onKeyDown} - onKeyPress={props.onKeyPress} - onKeyUp={props.onKeyUp} - // 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} - autoComplete={props.autoComplete} - aria-label={props["aria-label"]} - aria-labelledby={props["aria-labelledby"]} - maxLength={props.maxLength} - required={props.required} /> {props.icon && (
diff --git a/lib/core/src/utils/omit.ts b/lib/core/src/utils/omit.ts new file mode 100644 index 00000000..6e4d15e8 --- /dev/null +++ b/lib/core/src/utils/omit.ts @@ -0,0 +1,22 @@ +// https://stackoverflow.com/questions/53966509/typescript-type-safe-omit-function + +type Keys = (keyof T)[]; + +interface OmitFunction { + >(obj: T, keys: K): { + [K2 in Exclude]: 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; +}; diff --git a/lib/docs/src/components/input.stories.tsx b/lib/docs/src/components/input.stories.tsx index 6997e031..500871ad 100644 --- a/lib/docs/src/components/input.stories.tsx +++ b/lib/docs/src/components/input.stories.tsx @@ -11,39 +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"), - autoComplete: Utils.arg("string", "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"), }, }; From 06afb5f014f4090fab42e80b944ecaa0a4f33f98 Mon Sep 17 00:00:00 2001 From: Thien Do Date: Tue, 29 Jun 2021 04:23:40 +0700 Subject: [PATCH 3/3] Chore(core): Fix code style --- lib/core/src/input/input.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/src/input/input.tsx b/lib/core/src/input/input.tsx index 9d3bb4f5..a8c50297 100644 --- a/lib/core/src/input/input.tsx +++ b/lib/core/src/input/input.tsx @@ -30,7 +30,7 @@ export interface InputProps /** * The HTML type attribute, such as "email", "password" or "date". */ - type?: HTMLInputProps["type"], + type?: HTMLInputProps["type"]; /** * Initial value of the input in uncontrolled mode. */