Skip to content
This repository has been archived by the owner on May 4, 2022. It is now read-only.

fix(color-input-field): props were not passed down #20

Merged
merged 1 commit into from Oct 13, 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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
117 changes: 73 additions & 44 deletions atomic-design/molecules/color-input-field/src/color-input.tsx
Expand Up @@ -31,9 +31,11 @@ const hexKeys = new Set([
]);

const modKeys = new Set(["Backspace", "Tab", "ArrowLeft", "ArrowUp", "ArrowRight", "ArrowDown"]);

const copyAndPasteKeys = new Set(["KeyC", "KeyV", "KeyX"]);
function getSelectionText() {
const activeElement: HTMLInputElement | Element | null = document.activeElement;

const getSelectionText = () => {
const activeElement = document.activeElement;
if (activeElement.tagName === "INPUT") {
const { type, value, selectionStart, selectionEnd } = activeElement as HTMLInputElement;
const match = /^(?:text|search|password|tel|url)$/i.exec(type);
Expand All @@ -46,68 +48,95 @@ function getSelectionText() {
}

return "";
}
};

export const ColorInput = forwardRef<HTMLInputElement, ColorInputProps>(
({ defaultValue, width, fullWidth }, ref) => {
({ defaultValue, disabled, width, fullWidth, onChange, onBlur, onKeyDown, ...props }, ref) => {
const [value, setValue] = useState(defaultValue);
const [valid, setValid] = useState<boolean | null>(null);

const handleChange: ChangeEventHandler<HTMLInputElement> = useCallback(({ target }) => {
setValid(target.validity.valid);
if (target.value.length > 7 && target.value.startsWith("#")) {
setValid(true);
setValue(target.value.slice(0, 7));
} else if (target.value.length > 1 && target.value.startsWith("#")) {
setValue(target.value);
} else if (target.value === "#") {
setValid(null);
setValue("");
} else if (target.value.length === 0) {
setValue(target.value);
} else {
setValue(`#${target.value.slice(0, 6)}`);
}
}, []);
const handleChange: ChangeEventHandler<HTMLInputElement> = useCallback(
event_ => {
const { target } = event_;
setValid(target.validity.valid);
if (target.value.length > 7 && target.value.startsWith("#")) {
setValid(true);
const newValue = target.value.slice(0, 7);
setValue(newValue);
} else if (target.value.length > 1 && target.value.startsWith("#")) {
setValue(target.value);
} else if (target.value === "#") {
setValid(null);
setValue("");
} else if (target.value.length === 0) {
setValue(target.value);
} else {
setValue(`#${target.value.slice(0, 6)}`);
}

if (onChange) {
onChange(event_);
}
},
[onChange]
);

const handleKeyDown: KeyboardEventHandler<HTMLInputElement> = useCallback(
event_ => {
const selectionText = getSelectionText();
if (
!hexKeys.has(event_.code) &&
!modKeys.has(event_.code) &&
!(event_.metaKey && copyAndPasteKeys.has(event_.code))
) {
event_.preventDefault();
} else if (
!modKeys.has(event_.code) &&
((event_.currentTarget.value.length > 6 && selectionText.length === 0) ||
selectionText.includes("#"))
) {
event_.preventDefault();
}

const handleKeyDown: KeyboardEventHandler<HTMLInputElement> = useCallback(event_ => {
const selectionText = getSelectionText();
if (
!hexKeys.has(event_.code) &&
!modKeys.has(event_.code) &&
!(event_.metaKey && copyAndPasteKeys.has(event_.code))
) {
event_.preventDefault();
} else if (
!modKeys.has(event_.code) &&
((event_.currentTarget.value.length > 6 && selectionText.length === 0) ||
selectionText.includes("#"))
) {
event_.preventDefault();
}
}, []);
if (onKeyDown) {
onKeyDown(event_);
}
},
[onKeyDown]
);

const handleBlur: FocusEventHandler<HTMLInputElement> = useCallback(
event_ => {
const { target } = event_;
if (target.value.length === 4 && target.value.startsWith("#")) {
const [, _1, _2, _3] = target.value.split("");
setValid(true);
setValue(`#${_1}${_1}${_2}${_2}${_3}${_3}`);
}

const handleBlur: FocusEventHandler<HTMLInputElement> = useCallback(({ target }) => {
if (target.value.length === 4 && target.value.startsWith("#")) {
const [, _1, _2, _3] = target.value.split("");
setValid(true);
setValue(`#${_1}${_1}${_2}${_2}${_3}${_3}`);
}
}, []);
if (onBlur) {
onBlur(event_);
}
},
[onBlur]
);

return (
<StyledCombinedInput
isInvalid={valid === null ? undefined : !valid}
fullWidth={fullWidth}
width={width}
isDisabled={disabled}
>
<StyledInput
value={value}
{...props}
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
pattern="^#[a-f0-9]{6}"
value={value}
disabled={disabled}
invalid={valid === null ? undefined : !valid}
onChange={handleChange}
onKeyDown={handleKeyDown}
Expand Down
@@ -1,21 +1,36 @@
import { ColorInput, ColorInputProps } from "@dekk-ui/color-input-field";
import { withPseudo } from "@ergosign/storybook-addon-pseudo-states-react";
import { Story } from "@storybook/react";
import React from "react";

const Template: Story<ColorInputProps> = args => {
export const Simple: Story<ColorInputProps> = args => {
return <ColorInput {...args} />;
};

export const Simple = Template.bind({});

Simple.args = {
default: "#000000",
id: "opacity-0",
min: 0,
max: 100,
suffix: "%",
defaultValue: "#000000",
};

export const PseudoStates: Story<ColorInputProps> = args => {
return <ColorInput {...args} />;
};

PseudoStates.args = {
width: "120px",
};

PseudoStates.parameters = {
controls: { hideNoControlsWarning: true },
withPseudo: {
selector: "label",
pseudos: ["hover", "focus-within", "focus-within & hover"],
prefix: "pseudoclass--",
attributes: ["disabled"],
},
};

PseudoStates.decorators = [withPseudo];

const story = {
component: ColorInput,
title: "Design System/Molecules/ColorInput",
Expand Down