mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-12 08:25:44 +01:00
Checkbox and Chipinput have been improved, and a button has been added
Pushed-by: Konstantin Schaper<konstantin.schaper@cloudogu.com> Pushed-by: Tarik Gürsoy<tarik.guersoy@cloudogu.com> Co-authored-by: Konstantin Schaper<konstantin.schaper@cloudogu.com> Co-authored-by: Tarik Gürsoy<tarik.guersoy@cloudogu.com>
This commit is contained in:
@@ -25,20 +25,52 @@
|
||||
import React, { InputHTMLAttributes } from "react";
|
||||
import { createAttributesForTesting } from "@scm-manager/ui-components";
|
||||
import Help from "../base/help/Help";
|
||||
import styled from "styled-components";
|
||||
import classNames from "classnames";
|
||||
|
||||
const StyledInput = styled.input`
|
||||
height: 1rem;
|
||||
width: 1rem;
|
||||
`;
|
||||
|
||||
const StyledLabel = styled.label`
|
||||
margin-left: -0.75rem;
|
||||
display: inline-flex;
|
||||
`;
|
||||
|
||||
type InputFieldProps = {
|
||||
label: string;
|
||||
helpText?: string;
|
||||
testId?: string;
|
||||
labelClassName?: string;
|
||||
} & Omit<InputHTMLAttributes<HTMLInputElement>, "type">;
|
||||
|
||||
/**
|
||||
* @see https://bulma.io/documentation/form/checkbox/
|
||||
*/
|
||||
const Checkbox = React.forwardRef<HTMLInputElement, InputFieldProps>(
|
||||
({ readOnly, label, value, name, checked, defaultChecked, defaultValue, testId, helpText, ...props }, ref) => (
|
||||
// @ts-ignore bulma uses the disabled attribute on labels, although it is not part of the html spec
|
||||
<label className="checkbox" disabled={readOnly || props.disabled}>
|
||||
(
|
||||
{
|
||||
readOnly,
|
||||
label,
|
||||
className,
|
||||
labelClassName,
|
||||
value,
|
||||
name,
|
||||
checked,
|
||||
defaultChecked,
|
||||
defaultValue,
|
||||
testId,
|
||||
helpText,
|
||||
...props
|
||||
},
|
||||
ref
|
||||
) => (
|
||||
<StyledLabel
|
||||
className={classNames("checkbox is-align-items-center", labelClassName)}
|
||||
// @ts-ignore bulma uses the disabled attribute on labels, although it is not part of the html spec
|
||||
disabled={readOnly || props.disabled}
|
||||
>
|
||||
{readOnly ? (
|
||||
<>
|
||||
<input
|
||||
@@ -50,9 +82,9 @@ const Checkbox = React.forwardRef<HTMLInputElement, InputFieldProps>(
|
||||
defaultChecked={defaultChecked}
|
||||
readOnly
|
||||
/>
|
||||
<input
|
||||
<StyledInput
|
||||
type="checkbox"
|
||||
className="mr-1"
|
||||
className={classNames("m-3", className)}
|
||||
ref={ref}
|
||||
value={value}
|
||||
defaultValue={defaultValue}
|
||||
@@ -64,9 +96,9 @@ const Checkbox = React.forwardRef<HTMLInputElement, InputFieldProps>(
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<input
|
||||
<StyledInput
|
||||
type="checkbox"
|
||||
className="mr-1"
|
||||
className={classNames("m-3", className)}
|
||||
ref={ref}
|
||||
name={name}
|
||||
value={value}
|
||||
@@ -77,9 +109,10 @@ const Checkbox = React.forwardRef<HTMLInputElement, InputFieldProps>(
|
||||
{...createAttributesForTesting(testId)}
|
||||
/>
|
||||
)}
|
||||
|
||||
{label}
|
||||
{helpText ? <Help className="ml-1" text={helpText} /> : null}
|
||||
</label>
|
||||
</StyledLabel>
|
||||
)
|
||||
);
|
||||
export default Checkbox;
|
||||
|
||||
@@ -23,22 +23,28 @@
|
||||
*/
|
||||
|
||||
import { storiesOf } from "@storybook/react";
|
||||
import React, { useState } from "react";
|
||||
import React, { useRef, useState } from "react";
|
||||
import ChipInputField from "./ChipInputField";
|
||||
import Combobox from "../combobox/Combobox";
|
||||
import { Option } from "@scm-manager/ui-types";
|
||||
import ChipInput from "../headless-chip-input/ChipInput";
|
||||
|
||||
storiesOf("Chip Input Field", module)
|
||||
.add("Default", () => {
|
||||
const [value, setValue] = useState<Option<string>[]>([]);
|
||||
const ref = useRef<HTMLInputElement>(null);
|
||||
return (
|
||||
<ChipInputField
|
||||
value={value}
|
||||
onChange={setValue}
|
||||
label="Test Chips"
|
||||
placeholder="Type a new chip name and press enter to add"
|
||||
aria-label="My personal chip list"
|
||||
/>
|
||||
<>
|
||||
<ChipInputField
|
||||
value={value}
|
||||
onChange={setValue}
|
||||
label="Test Chips"
|
||||
placeholder="Type a new chip name and press enter to add"
|
||||
aria-label="My personal chip list"
|
||||
ref={ref}
|
||||
/>
|
||||
<ChipInput.AddButton inputRef={ref}>Add</ChipInput.AddButton>
|
||||
</>
|
||||
);
|
||||
})
|
||||
.add("With Autocomplete", () => {
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import React, { KeyboardEventHandler, ReactElement, useCallback } from "react";
|
||||
import React, { KeyboardEventHandler, PropsWithRef, ReactElement, Ref, RefObject, useCallback } from "react";
|
||||
import { createAttributesForTesting, useGeneratedId } from "@scm-manager/ui-components";
|
||||
import Field from "../base/Field";
|
||||
import Label from "../base/label/Label";
|
||||
@@ -81,6 +81,7 @@ type InputFieldProps<T> = {
|
||||
className?: string;
|
||||
isLoading?: boolean;
|
||||
isNewItemDuplicate?: (existingItem: Option<T>, newItem: Option<T>) => boolean;
|
||||
ref?: Ref<HTMLInputElement>;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -105,7 +106,7 @@ const ChipInputField = function ChipInputField<T>(
|
||||
isLoading,
|
||||
isNewItemDuplicate,
|
||||
...props
|
||||
}: InputFieldProps<T>,
|
||||
}: PropsWithRef<InputFieldProps<T>>,
|
||||
ref: React.ForwardedRef<HTMLInputElement>
|
||||
) {
|
||||
const [t] = useTranslation("commons", { keyPrefix: "form.chipList" });
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
import React, {
|
||||
ButtonHTMLAttributes,
|
||||
ComponentProps,
|
||||
ComponentType,
|
||||
Context,
|
||||
createContext,
|
||||
@@ -40,6 +41,7 @@ import React, {
|
||||
import { Slot } from "@radix-ui/react-slot";
|
||||
import { Option } from "@scm-manager/ui-types";
|
||||
import { mergeRefs, withForwardRef } from "../helpers";
|
||||
import { Button } from "@scm-manager/ui-buttons";
|
||||
|
||||
type ChipInputContextType<T> = {
|
||||
add(newValue: Option<T>): void;
|
||||
@@ -215,8 +217,21 @@ const ChipInput = withForwardRef(function ChipInput<T>(
|
||||
);
|
||||
});
|
||||
|
||||
const AddButton = React.forwardRef<
|
||||
HTMLButtonElement,
|
||||
Omit<ComponentProps<typeof Button>, "onClick"> & { inputRef: RefObject<HTMLInputElement | null> }
|
||||
>(({ inputRef, children, ...props }, ref) => (
|
||||
<Button
|
||||
{...props}
|
||||
onClick={() => inputRef.current?.dispatchEvent(new KeyboardEvent("keydown", { key: "Enter", bubbles: true }))}
|
||||
>
|
||||
{children}
|
||||
</Button>
|
||||
));
|
||||
|
||||
export default Object.assign(ChipInput, {
|
||||
Chip: Object.assign(Chip, {
|
||||
Delete: ChipDelete,
|
||||
}),
|
||||
AddButton,
|
||||
});
|
||||
|
||||
@@ -36,17 +36,23 @@ import ControlledColumn from "./table/ControlledColumn";
|
||||
import AddListEntryForm from "./AddListEntryForm";
|
||||
import { ScmNestedFormPathContextProvider } from "./FormPathContext";
|
||||
import ControlledComboboxField from "./combobox/ControlledComboboxField";
|
||||
import ChipInputFieldComponent from "./chip-input/ChipInputField";
|
||||
import ChipInput from "./headless-chip-input/ChipInput";
|
||||
|
||||
export { default as Checkbox } from "./checkbox/Checkbox";
|
||||
export { default as Combobox } from "./combobox/Combobox";
|
||||
export { default as ConfigurationForm } from "./ConfigurationForm";
|
||||
export { default as SelectField } from "./select/SelectField";
|
||||
export { default as ChipInputField } from "./chip-input/ChipInputField";
|
||||
export { default as ComboboxField } from "./combobox/ComboboxField";
|
||||
export { default as Input } from "./input/Input";
|
||||
export { default as Select } from "./select/Select";
|
||||
export * from "./resourceHooks";
|
||||
export { default as Label } from "./base/label/Label";
|
||||
|
||||
export const ChipInputField = Object.assign(ChipInputFieldComponent, {
|
||||
AddButton: ChipInput.AddButton,
|
||||
});
|
||||
|
||||
export const Form = Object.assign(FormCmp, {
|
||||
Row: FormRow,
|
||||
Input: ControlledInputField,
|
||||
|
||||
Reference in New Issue
Block a user