Skip to content

Commit

Permalink
feat: limited input component
Browse files Browse the repository at this point in the history
  • Loading branch information
ndv99 committed Apr 30, 2024
1 parent c022141 commit 8ab4057
Show file tree
Hide file tree
Showing 7 changed files with 136 additions and 2 deletions.
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions src/lib/elements/LimitedInput/LimitedInput.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
@import "vanilla-framework";

.limited-input {
position: relative;

&__wrapper::before {
position: absolute;
pointer-events: none;
padding-left: $spv--small;
padding-bottom: calc(0.4rem - 1px);
padding-top: calc(0.4rem - 1px);
content: var(--immutable, "");
top: var(--top, "inherit");
}
}
20 changes: 20 additions & 0 deletions src/lib/elements/LimitedInput/LimitedInput.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import type { Meta, StoryObj } from "@storybook/react";

import { LimitedInput } from "@/lib/elements/LimitedInput/LimitedInput";

const meta: Meta<typeof LimitedInput> = {
title: "elements/LimitedInput",
component: LimitedInput,
tags: ["autodocs"],
};

export default meta;

export const Example: StoryObj<typeof LimitedInput> = {
args: {
help: "The valid range for this subnet is 192.168.0.[1-254]",
label: "IP address",
immutableText: "192.168.0.",
placeholder: "[1-254]",
},
};
38 changes: 38 additions & 0 deletions src/lib/elements/LimitedInput/LimitedInput.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { render, screen } from "@testing-library/react";

import { LimitedInput } from "./LimitedInput";

const { getComputedStyle } = window;

beforeAll(() => {
// getComputedStyle is not implemeneted in jsdom, so we need to do this.
window.getComputedStyle = (elt) => getComputedStyle(elt);
});

afterAll(() => {
// Reset to original implementation
window.getComputedStyle = getComputedStyle;
});

it("renders without crashing", async () => {
render(<LimitedInput aria-label="Limited input" immutableText="Some text" />);

expect(
screen.getByRole("textbox", { name: "Limited input" }),
).toBeInTheDocument();
});

it("sets the --immutable css variable to the provided immutable text", async () => {
const { rerender } = render(
<LimitedInput aria-label="Limited input" immutableText="Some text" />,
);

rerender(
<LimitedInput aria-label="Limited input" immutableText="Some text" />,
);

expect(
screen.getByRole("textbox", { name: "Limited input" }).parentElement
?.parentElement,
).toHaveStyle(`--immutable: "Some text";`);
});
59 changes: 59 additions & 0 deletions src/lib/elements/LimitedInput/LimitedInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { RefObject, useEffect, useRef } from "react";

import { Input, InputProps } from "@canonical/react-components";
import "./LimitedInput.scss";
import classNames from "classnames";

export type LimitedInputProps = Omit<InputProps, "type"> & {
immutableText: string;
};

/**
* An input component with a fixed prefix that cannot be edited.
*
* @param immutableText The prefixed text that cannot be edited.
* @returns A LimitedInput component.
*/
export const LimitedInput = ({
immutableText,
...props
}: LimitedInputProps) => {
const limitedInputRef: RefObject<HTMLDivElement> = useRef(null);

const inputWrapper = limitedInputRef.current?.firstElementChild;

useEffect(() => {
if (inputWrapper) {
console.log("AHAHAHAHAHHA");
if (props.label) {
inputWrapper.setAttribute(
"style",
`--top: 2.5rem; --immutable: "${immutableText}"`,
);
} else {
inputWrapper.setAttribute("style", `--immutable: "${immutableText}"`);
}

const width = window.getComputedStyle(inputWrapper, ":before").width;

inputWrapper.lastElementChild?.firstElementChild?.setAttribute(
"style",
`padding-left: ${width}`,
);
}
}, [inputWrapper]);

return (
<div className="limited-input" ref={limitedInputRef}>
<Input
className={classNames("limited-input__input", props.className)}
type="text"
wrapperClassName={classNames(
"limited-input__wrapper",
props.wrapperClassName,
)}
{...props}
/>
</div>
);
};
1 change: 1 addition & 0 deletions src/lib/elements/LimitedInput/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./LimitedInput";
3 changes: 2 additions & 1 deletion src/lib/elements/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ export * from "./Meter";
export * from "./ExternalLink";

export * from "./ProgressIndicator";
export * from "./Placeholder";
export * from "./Placeholder";
export * from "./LimitedInput";

0 comments on commit 8ab4057

Please sign in to comment.