From 830286c332cd9b0270b5ed6a581e8a2107ccb7b5 Mon Sep 17 00:00:00 2001 From: Chaim Lev-Ari Date: Tue, 23 Nov 2021 07:16:50 +0200 Subject: [PATCH] feat(app): introduce input-group component [EE-2062] (#6135) --- app/assets/css/app.css | 4 + .../form-components/Input/NumberInput.tsx | 2 + .../form-components/Input/Select.tsx | 2 + .../form-components/Input/TextInput.tsx | 2 + .../form-components/Input/Textarea.tsx | 6 + .../components/form-components/Input/types.ts | 1 + .../InputGroup/InputGroup.stories.tsx | 108 ++++++++++++++++++ .../form-components/InputGroup/InputGroup.tsx | 37 ++++++ .../InputGroup/InputGroupAddon.tsx | 9 ++ .../InputGroup/InputGroupButtonWrapper.tsx | 11 ++ .../form-components/InputGroup/index.ts | 23 ++++ 11 files changed, 205 insertions(+) create mode 100644 app/portainer/components/form-components/InputGroup/InputGroup.stories.tsx create mode 100644 app/portainer/components/form-components/InputGroup/InputGroup.tsx create mode 100644 app/portainer/components/form-components/InputGroup/InputGroupAddon.tsx create mode 100644 app/portainer/components/form-components/InputGroup/InputGroupButtonWrapper.tsx create mode 100644 app/portainer/components/form-components/InputGroup/index.ts diff --git a/app/assets/css/app.css b/app/assets/css/app.css index 700e887df..91b058fe2 100644 --- a/app/assets/css/app.css +++ b/app/assets/css/app.css @@ -836,6 +836,10 @@ json-tree .branch-preview { align-items: center; } +.space-y-8 > * + * { + margin-top: 2rem; +} + .my-8 { margin-top: 2rem; margin-bottom: 2rem; diff --git a/app/portainer/components/form-components/Input/NumberInput.tsx b/app/portainer/components/form-components/Input/NumberInput.tsx index a05d909cf..5a583b51c 100644 --- a/app/portainer/components/form-components/Input/NumberInput.tsx +++ b/app/portainer/components/form-components/Input/NumberInput.tsx @@ -16,6 +16,7 @@ export function NumberInput({ value, className, readonly, + placeholder, onChange, }: Props) { return ( @@ -27,6 +28,7 @@ export function NumberInput({ disabled={disabled} readonly={readonly} required={required} + placeholder={placeholder} onChange={(value) => onChange(parseFloat(value))} /> ); diff --git a/app/portainer/components/form-components/Input/Select.tsx b/app/portainer/components/form-components/Input/Select.tsx index d6776bd79..ea00f7d32 100644 --- a/app/portainer/components/form-components/Input/Select.tsx +++ b/app/portainer/components/form-components/Input/Select.tsx @@ -20,6 +20,7 @@ export function Select({ disabled, id, required, + placeholder, }: Props) { return ( + + + + + ); +} + +export function Sizing() { + const [value, setValue] = useState(''); + return ( +
+ + Small + + + + + Default + + + + + Large + + +
+ ); +} diff --git a/app/portainer/components/form-components/InputGroup/InputGroup.tsx b/app/portainer/components/form-components/InputGroup/InputGroup.tsx new file mode 100644 index 000000000..a3111b578 --- /dev/null +++ b/app/portainer/components/form-components/InputGroup/InputGroup.tsx @@ -0,0 +1,37 @@ +import clsx from 'clsx'; +import { createContext, PropsWithChildren, useContext } from 'react'; + +const Context = createContext(null); + +type Size = 'small' | 'large'; + +export function useInputGroupContext() { + const context = useContext(Context); + + if (context == null) { + throw new Error('Should be inside a InputGroup component'); + } +} + +interface Props { + size?: Size; +} + +export function InputGroup({ children, size }: PropsWithChildren) { + return ( + +
{children}
+
+ ); +} + +function sizeClass(size?: Size) { + switch (size) { + case 'large': + return 'input-group-lg'; + case 'small': + return 'input-group-sm'; + default: + return ''; + } +} diff --git a/app/portainer/components/form-components/InputGroup/InputGroupAddon.tsx b/app/portainer/components/form-components/InputGroup/InputGroupAddon.tsx new file mode 100644 index 000000000..32d294367 --- /dev/null +++ b/app/portainer/components/form-components/InputGroup/InputGroupAddon.tsx @@ -0,0 +1,9 @@ +import { PropsWithChildren } from 'react'; + +import { useInputGroupContext } from './InputGroup'; + +export function InputGroupAddon({ children }: PropsWithChildren) { + useInputGroupContext(); + + return {children}; +} diff --git a/app/portainer/components/form-components/InputGroup/InputGroupButtonWrapper.tsx b/app/portainer/components/form-components/InputGroup/InputGroupButtonWrapper.tsx new file mode 100644 index 000000000..a09ab254c --- /dev/null +++ b/app/portainer/components/form-components/InputGroup/InputGroupButtonWrapper.tsx @@ -0,0 +1,11 @@ +import { PropsWithChildren } from 'react'; + +import { useInputGroupContext } from './InputGroup'; + +export function InputGroupButtonWrapper({ + children, +}: PropsWithChildren) { + useInputGroupContext(); + + return {children}; +} diff --git a/app/portainer/components/form-components/InputGroup/index.ts b/app/portainer/components/form-components/InputGroup/index.ts new file mode 100644 index 000000000..f9c5cb7d4 --- /dev/null +++ b/app/portainer/components/form-components/InputGroup/index.ts @@ -0,0 +1,23 @@ +import { NumberInput, TextInput } from '../Input'; + +import { InputGroup as MainComponent } from './InputGroup'; +import { InputGroupAddon } from './InputGroupAddon'; +import { InputGroupButtonWrapper } from './InputGroupButtonWrapper'; + +interface InputGroupSubComponents { + Addon: typeof InputGroupAddon; + ButtonWrapper: typeof InputGroupButtonWrapper; + Input: typeof TextInput; + NumberInput: typeof NumberInput; +} + +const InputGroup: typeof MainComponent & + InputGroupSubComponents = MainComponent as typeof MainComponent & + InputGroupSubComponents; + +InputGroup.Addon = InputGroupAddon; +InputGroup.ButtonWrapper = InputGroupButtonWrapper; +InputGroup.Input = TextInput; +InputGroup.NumberInput = NumberInput; + +export { InputGroup };