feat: add New Object Custom form (#2105)
* feat: add New Object Custom form Closes #1808 * fix: fix lint error
This commit is contained in:
@ -1,14 +1,22 @@
|
|||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
import { H2Title } from '@/ui/display/typography/components/H2Title';
|
import { H2Title } from '@/ui/display/typography/components/H2Title';
|
||||||
import { AutosizeTextInput } from '@/ui/input/components/AutosizeTextInput';
|
import { TextArea } from '@/ui/input/components/TextArea';
|
||||||
import { TextInput } from '@/ui/input/components/TextInput';
|
import { TextInput } from '@/ui/input/components/TextInput';
|
||||||
import { Section } from '@/ui/layout/section/components/Section';
|
import { Section } from '@/ui/layout/section/components/Section';
|
||||||
|
|
||||||
type SettingsObjectFormSectionProps = {
|
type SettingsObjectFormSectionProps = {
|
||||||
|
disabled?: boolean;
|
||||||
singularName?: string;
|
singularName?: string;
|
||||||
pluralName?: string;
|
pluralName?: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
|
onChange?: (
|
||||||
|
formValues: Partial<{
|
||||||
|
singularName: string;
|
||||||
|
pluralName: string;
|
||||||
|
description: string;
|
||||||
|
}>,
|
||||||
|
) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const StyledInputsContainer = styled.div`
|
const StyledInputsContainer = styled.div`
|
||||||
@ -23,9 +31,11 @@ const StyledTextInput = styled(TextInput)`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export const SettingsObjectFormSection = ({
|
export const SettingsObjectFormSection = ({
|
||||||
singularName,
|
disabled,
|
||||||
pluralName,
|
singularName = '',
|
||||||
description,
|
pluralName = '',
|
||||||
|
description = '',
|
||||||
|
onChange,
|
||||||
}: SettingsObjectFormSectionProps) => (
|
}: SettingsObjectFormSectionProps) => (
|
||||||
<Section>
|
<Section>
|
||||||
<H2Title
|
<H2Title
|
||||||
@ -35,19 +45,25 @@ export const SettingsObjectFormSection = ({
|
|||||||
<StyledInputsContainer>
|
<StyledInputsContainer>
|
||||||
<StyledTextInput
|
<StyledTextInput
|
||||||
label="Singular"
|
label="Singular"
|
||||||
placeholder="Invoice"
|
placeholder="Investor"
|
||||||
value={singularName}
|
value={singularName}
|
||||||
|
onChange={(value) => onChange?.({ singularName: value })}
|
||||||
|
disabled={disabled}
|
||||||
/>
|
/>
|
||||||
<StyledTextInput
|
<StyledTextInput
|
||||||
label="Plural"
|
label="Plural"
|
||||||
placeholder="Invoices"
|
placeholder="Investors"
|
||||||
value={pluralName}
|
value={pluralName}
|
||||||
|
onChange={(value) => onChange?.({ pluralName: value })}
|
||||||
|
disabled={disabled}
|
||||||
/>
|
/>
|
||||||
</StyledInputsContainer>
|
</StyledInputsContainer>
|
||||||
<AutosizeTextInput
|
<TextArea
|
||||||
placeholder="Write a description"
|
placeholder="Write a description"
|
||||||
minRows={4}
|
minRows={4}
|
||||||
value={description}
|
value={description}
|
||||||
|
onChange={(value) => onChange?.({ description: value })}
|
||||||
|
disabled={disabled}
|
||||||
/>
|
/>
|
||||||
</Section>
|
</Section>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
import { useState } from 'react';
|
|
||||||
import { useTheme } from '@emotion/react';
|
import { useTheme } from '@emotion/react';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
@ -6,6 +5,13 @@ import { IconBox, IconDatabase, IconFileCheck } from '@/ui/display/icon';
|
|||||||
|
|
||||||
import { SettingsObjectTypeCard } from './SettingsObjectTypeCard';
|
import { SettingsObjectTypeCard } from './SettingsObjectTypeCard';
|
||||||
|
|
||||||
|
export type NewObjectType = 'Standard' | 'Custom' | 'Remote';
|
||||||
|
|
||||||
|
type SettingsNewObjectTypeProps = {
|
||||||
|
selectedType?: NewObjectType;
|
||||||
|
onTypeSelect?: (type: NewObjectType) => void;
|
||||||
|
};
|
||||||
|
|
||||||
const StyledContainer = styled.div`
|
const StyledContainer = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
@ -13,12 +19,11 @@ const StyledContainer = styled.div`
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const SettingsNewObjectType = () => {
|
export const SettingsNewObjectType = ({
|
||||||
|
selectedType = 'Standard',
|
||||||
|
onTypeSelect,
|
||||||
|
}: SettingsNewObjectTypeProps) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const [selectedType, setSelectedType] = useState<string | null>(null);
|
|
||||||
const handleCardClick = (selectedType: string) => {
|
|
||||||
setSelectedType(selectedType);
|
|
||||||
};
|
|
||||||
return (
|
return (
|
||||||
<StyledContainer>
|
<StyledContainer>
|
||||||
<SettingsObjectTypeCard
|
<SettingsObjectTypeCard
|
||||||
@ -32,8 +37,8 @@ export const SettingsNewObjectType = () => {
|
|||||||
color={theme.font.color.tertiary}
|
color={theme.font.color.tertiary}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
onClick={() => handleCardClick('Standard')}
|
onClick={() => onTypeSelect?.('Standard')}
|
||||||
></SettingsObjectTypeCard>
|
/>
|
||||||
<SettingsObjectTypeCard
|
<SettingsObjectTypeCard
|
||||||
title="Custom"
|
title="Custom"
|
||||||
color="orange"
|
color="orange"
|
||||||
@ -45,8 +50,8 @@ export const SettingsNewObjectType = () => {
|
|||||||
color={theme.font.color.tertiary}
|
color={theme.font.color.tertiary}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
onClick={() => handleCardClick('Custom')}
|
onClick={() => onTypeSelect?.('Custom')}
|
||||||
></SettingsObjectTypeCard>
|
/>
|
||||||
<SettingsObjectTypeCard
|
<SettingsObjectTypeCard
|
||||||
title="Remote"
|
title="Remote"
|
||||||
soon
|
soon
|
||||||
@ -60,7 +65,7 @@ export const SettingsNewObjectType = () => {
|
|||||||
color={theme.font.color.tertiary}
|
color={theme.font.color.tertiary}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
></SettingsObjectTypeCard>
|
/>
|
||||||
</StyledContainer>
|
</StyledContainer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -58,21 +58,19 @@ export const SettingsObjectTypeCard = ({
|
|||||||
}: SettingsObjectTypeCardProps) => {
|
}: SettingsObjectTypeCardProps) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
return (
|
return (
|
||||||
<div onClick={() => onclick}>
|
<StyledObjectTypeCard
|
||||||
<StyledObjectTypeCard
|
title={title}
|
||||||
title={title}
|
soon={soon}
|
||||||
soon={soon}
|
disabled={disabled}
|
||||||
disabled={disabled}
|
color={color}
|
||||||
color={color}
|
selected={selected}
|
||||||
selected={selected}
|
onClick={onClick}
|
||||||
onClick={onClick}
|
>
|
||||||
>
|
{prefixIcon}
|
||||||
{prefixIcon}
|
<StyledTag color={color} text={title} />
|
||||||
<StyledTag color={color} text={title} />
|
{soon && <SoonPill />}
|
||||||
{soon && <SoonPill />}
|
{!disabled && selected && <StyledIconCheck size={theme.icon.size.md} />}
|
||||||
{!disabled && selected && <StyledIconCheck size={theme.icon.size.md} />}
|
</StyledObjectTypeCard>
|
||||||
</StyledObjectTypeCard>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
|
import { IconPigMoney } from '@/ui/display/icon';
|
||||||
import { IconComponent } from '@/ui/display/icon/types/IconComponent';
|
import { IconComponent } from '@/ui/display/icon/types/IconComponent';
|
||||||
import { H2Title } from '@/ui/display/typography/components/H2Title';
|
import { H2Title } from '@/ui/display/typography/components/H2Title';
|
||||||
import { IconPicker } from '@/ui/input/components/IconPicker';
|
import { IconPicker } from '@/ui/input/components/IconPicker';
|
||||||
@ -23,15 +24,19 @@ const StyledArrowContainer = styled.div`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
type SettingsObjectIconSectionProps = {
|
type SettingsObjectIconSectionProps = {
|
||||||
Icon: IconComponent;
|
disabled?: boolean;
|
||||||
iconKey: string;
|
Icon?: IconComponent;
|
||||||
setIconPicker?: (icon: { Icon: IconComponent; iconKey: string }) => void;
|
iconKey?: string;
|
||||||
|
label?: string;
|
||||||
|
onChange?: (icon: { Icon: IconComponent; iconKey: string }) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SettingsObjectIconSection = ({
|
export const SettingsObjectIconSection = ({
|
||||||
Icon,
|
disabled,
|
||||||
iconKey,
|
Icon = IconPigMoney,
|
||||||
setIconPicker,
|
iconKey = 'IconPigMoney',
|
||||||
|
label,
|
||||||
|
onChange,
|
||||||
}: SettingsObjectIconSectionProps) => {
|
}: SettingsObjectIconSectionProps) => {
|
||||||
return (
|
return (
|
||||||
<Section>
|
<Section>
|
||||||
@ -41,15 +46,16 @@ export const SettingsObjectIconSection = ({
|
|||||||
/>
|
/>
|
||||||
<StyledContainer>
|
<StyledContainer>
|
||||||
<IconPicker
|
<IconPicker
|
||||||
|
disabled={disabled}
|
||||||
selectedIconKey={iconKey}
|
selectedIconKey={iconKey}
|
||||||
onChange={(icon) => {
|
onChange={(icon) => {
|
||||||
setIconPicker?.({ Icon: icon.Icon, iconKey: icon.iconKey });
|
onChange?.({ Icon: icon.Icon, iconKey: icon.iconKey });
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<StyledArrowContainer>
|
<StyledArrowContainer>
|
||||||
<img src={ArrowRight} alt="Arrow right" width={32} height={16} />
|
<img src={ArrowRight} alt="Arrow right" width={32} height={16} />
|
||||||
</StyledArrowContainer>
|
</StyledArrowContainer>
|
||||||
<SettingsObjectIconWithLabel Icon={Icon} label="Workspaces" />
|
<SettingsObjectIconWithLabel Icon={Icon} label={label || 'Investors'} />
|
||||||
</StyledContainer>
|
</StyledContainer>
|
||||||
</Section>
|
</Section>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -69,6 +69,7 @@ export {
|
|||||||
IconNumbers,
|
IconNumbers,
|
||||||
IconPencil,
|
IconPencil,
|
||||||
IconPhone,
|
IconPhone,
|
||||||
|
IconPigMoney,
|
||||||
IconPlane,
|
IconPlane,
|
||||||
IconPlug,
|
IconPlug,
|
||||||
IconPlus,
|
IconPlus,
|
||||||
|
|||||||
@ -17,6 +17,7 @@ import { DropdownMenuSkeletonItem } from '../relation-picker/components/skeleton
|
|||||||
import { IconPickerHotkeyScope } from '../types/IconPickerHotkeyScope';
|
import { IconPickerHotkeyScope } from '../types/IconPickerHotkeyScope';
|
||||||
|
|
||||||
type IconPickerProps = {
|
type IconPickerProps = {
|
||||||
|
disabled?: boolean;
|
||||||
onChange: (params: { iconKey: string; Icon: IconComponent }) => void;
|
onChange: (params: { iconKey: string; Icon: IconComponent }) => void;
|
||||||
selectedIconKey?: string;
|
selectedIconKey?: string;
|
||||||
onClickOutside?: () => void;
|
onClickOutside?: () => void;
|
||||||
@ -39,6 +40,7 @@ const convertIconKeyToLabel = (iconKey: string) =>
|
|||||||
iconKey.replace(/[A-Z]/g, (letter) => ` ${letter}`).trim();
|
iconKey.replace(/[A-Z]/g, (letter) => ` ${letter}`).trim();
|
||||||
|
|
||||||
export const IconPicker = ({
|
export const IconPicker = ({
|
||||||
|
disabled,
|
||||||
onChange,
|
onChange,
|
||||||
selectedIconKey,
|
selectedIconKey,
|
||||||
onClickOutside,
|
onClickOutside,
|
||||||
@ -81,6 +83,7 @@ export const IconPicker = ({
|
|||||||
dropdownHotkeyScope={{ scope: IconPickerHotkeyScope.IconPicker }}
|
dropdownHotkeyScope={{ scope: IconPickerHotkeyScope.IconPicker }}
|
||||||
clickableComponent={
|
clickableComponent={
|
||||||
<IconButton
|
<IconButton
|
||||||
|
disabled={disabled}
|
||||||
Icon={selectedIconKey ? icons[selectedIconKey] : IconApps}
|
Icon={selectedIconKey ? icons[selectedIconKey] : IconApps}
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
/>
|
/>
|
||||||
@ -100,6 +103,7 @@ export const IconPicker = ({
|
|||||||
<StyledMenuIconItemsContainer>
|
<StyledMenuIconItemsContainer>
|
||||||
{iconKeys.map((iconKey) => (
|
{iconKeys.map((iconKey) => (
|
||||||
<StyledLightIconButton
|
<StyledLightIconButton
|
||||||
|
key={iconKey}
|
||||||
aria-label={convertIconKeyToLabel(iconKey)}
|
aria-label={convertIconKeyToLabel(iconKey)}
|
||||||
isSelected={selectedIconKey === iconKey}
|
isSelected={selectedIconKey === iconKey}
|
||||||
size="medium"
|
size="medium"
|
||||||
|
|||||||
63
front/src/modules/ui/input/components/TextArea.tsx
Normal file
63
front/src/modules/ui/input/components/TextArea.tsx
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
import TextareaAutosize from 'react-textarea-autosize';
|
||||||
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
|
const MAX_ROWS = 5;
|
||||||
|
|
||||||
|
export type TextAreaProps = {
|
||||||
|
disabled?: boolean;
|
||||||
|
minRows?: number;
|
||||||
|
onChange?: (value: string) => void;
|
||||||
|
placeholder?: string;
|
||||||
|
value?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const StyledTextArea = styled(TextareaAutosize)`
|
||||||
|
background-color: ${({ theme }) => theme.background.transparent.lighter};
|
||||||
|
border: 1px solid ${({ theme }) => theme.border.color.medium};
|
||||||
|
border-radius: ${({ theme }) => theme.border.radius.sm};
|
||||||
|
box-sizing: border-box;
|
||||||
|
color: ${({ theme }) => theme.font.color.primary};
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: ${({ theme }) => theme.font.size.md};
|
||||||
|
font-weight: ${({ theme }) => theme.font.weight.regular};
|
||||||
|
line-height: 16px;
|
||||||
|
overflow: auto;
|
||||||
|
padding: ${({ theme }) => theme.spacing(2)};
|
||||||
|
padding-top: ${({ theme }) => theme.spacing(3)};
|
||||||
|
resize: none;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::placeholder {
|
||||||
|
color: ${({ theme }) => theme.font.color.light};
|
||||||
|
font-weight: ${({ theme }) => theme.font.weight.regular};
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
color: ${({ theme }) => theme.font.color.tertiary};
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const TextArea = ({
|
||||||
|
disabled,
|
||||||
|
placeholder,
|
||||||
|
minRows = 1,
|
||||||
|
value = '',
|
||||||
|
onChange,
|
||||||
|
}: TextAreaProps) => {
|
||||||
|
const computedMinRows = Math.min(minRows, MAX_ROWS);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<StyledTextArea
|
||||||
|
placeholder={placeholder}
|
||||||
|
maxRows={MAX_ROWS}
|
||||||
|
minRows={computedMinRows}
|
||||||
|
value={value}
|
||||||
|
onChange={(event) => onChange?.(event.target.value)}
|
||||||
|
disabled={disabled}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -19,7 +19,7 @@ import { useCombinedRefs } from '~/hooks/useCombinedRefs';
|
|||||||
|
|
||||||
import { InputHotkeyScope } from '../types/InputHotkeyScope';
|
import { InputHotkeyScope } from '../types/InputHotkeyScope';
|
||||||
|
|
||||||
type TextInputComponentProps = Omit<
|
export type TextInputComponentProps = Omit<
|
||||||
InputHTMLAttributes<HTMLInputElement>,
|
InputHTMLAttributes<HTMLInputElement>,
|
||||||
'onChange'
|
'onChange'
|
||||||
> & {
|
> & {
|
||||||
@ -53,9 +53,10 @@ const StyledInputContainer = styled.div`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
const StyledInput = styled.input<Pick<TextInputComponentProps, 'fullWidth'>>`
|
const StyledInput = styled.input<Pick<TextInputComponentProps, 'fullWidth'>>`
|
||||||
background-color: ${({ theme }) => theme.background.tertiary};
|
background-color: ${({ theme }) => theme.background.transparent.lighter};
|
||||||
border: none;
|
border: 1px solid ${({ theme }) => theme.border.color.medium};
|
||||||
border-bottom-left-radius: ${({ theme }) => theme.border.radius.sm};
|
border-bottom-left-radius: ${({ theme }) => theme.border.radius.sm};
|
||||||
|
border-right: none;
|
||||||
border-top-left-radius: ${({ theme }) => theme.border.radius.sm};
|
border-top-left-radius: ${({ theme }) => theme.border.radius.sm};
|
||||||
color: ${({ theme }) => theme.font.color.primary};
|
color: ${({ theme }) => theme.font.color.primary};
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -74,6 +75,10 @@ const StyledInput = styled.input<Pick<TextInputComponentProps, 'fullWidth'>>`
|
|||||||
font-family: ${({ theme }) => theme.font.family};
|
font-family: ${({ theme }) => theme.font.family};
|
||||||
font-weight: ${({ theme }) => theme.font.weight.medium};
|
font-weight: ${({ theme }) => theme.font.weight.medium};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
color: ${({ theme }) => theme.font.color.tertiary};
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const StyledErrorHelper = styled.div`
|
const StyledErrorHelper = styled.div`
|
||||||
@ -84,8 +89,10 @@ const StyledErrorHelper = styled.div`
|
|||||||
|
|
||||||
const StyledTrailingIconContainer = styled.div`
|
const StyledTrailingIconContainer = styled.div`
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background-color: ${({ theme }) => theme.background.tertiary};
|
background-color: ${({ theme }) => theme.background.transparent.lighter};
|
||||||
|
border: 1px solid ${({ theme }) => theme.border.color.medium};
|
||||||
border-bottom-right-radius: ${({ theme }) => theme.border.radius.sm};
|
border-bottom-right-radius: ${({ theme }) => theme.border.radius.sm};
|
||||||
|
border-left: none;
|
||||||
border-top-right-radius: ${({ theme }) => theme.border.radius.sm};
|
border-top-right-radius: ${({ theme }) => theme.border.radius.sm};
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|||||||
@ -0,0 +1,40 @@
|
|||||||
|
import { useState } from 'react';
|
||||||
|
import { Meta, StoryObj } from '@storybook/react';
|
||||||
|
|
||||||
|
import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator';
|
||||||
|
|
||||||
|
import { TextArea, TextAreaProps } from '../TextArea';
|
||||||
|
|
||||||
|
type RenderProps = TextAreaProps;
|
||||||
|
|
||||||
|
const Render = (args: RenderProps) => {
|
||||||
|
const [value, setValue] = useState(args.value);
|
||||||
|
const handleChange = (text: string) => {
|
||||||
|
args.onChange?.(text);
|
||||||
|
setValue(text);
|
||||||
|
};
|
||||||
|
|
||||||
|
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||||
|
return <TextArea {...args} value={value} onChange={handleChange} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
const meta: Meta<typeof TextArea> = {
|
||||||
|
title: 'UI/Input/TextArea',
|
||||||
|
component: TextArea,
|
||||||
|
decorators: [ComponentDecorator],
|
||||||
|
args: { minRows: 4, placeholder: 'Lorem Ipsum' },
|
||||||
|
render: Render,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default meta;
|
||||||
|
type Story = StoryObj<typeof TextArea>;
|
||||||
|
|
||||||
|
export const Default: Story = {};
|
||||||
|
|
||||||
|
export const Filled: Story = {
|
||||||
|
args: { value: 'Lorem Ipsum' },
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Disabled: Story = {
|
||||||
|
args: { disabled: true, value: 'Lorem Ipsum' },
|
||||||
|
};
|
||||||
@ -0,0 +1,40 @@
|
|||||||
|
import { useState } from 'react';
|
||||||
|
import { Meta, StoryObj } from '@storybook/react';
|
||||||
|
|
||||||
|
import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator';
|
||||||
|
|
||||||
|
import { TextInput, TextInputComponentProps } from '../TextInput';
|
||||||
|
|
||||||
|
type RenderProps = TextInputComponentProps;
|
||||||
|
|
||||||
|
const Render = (args: RenderProps) => {
|
||||||
|
const [value, setValue] = useState(args.value);
|
||||||
|
const handleChange = (text: string) => {
|
||||||
|
args.onChange?.(text);
|
||||||
|
setValue(text);
|
||||||
|
};
|
||||||
|
|
||||||
|
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||||
|
return <TextInput {...args} value={value} onChange={handleChange} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
const meta: Meta<typeof TextInput> = {
|
||||||
|
title: 'UI/Input/TextInput',
|
||||||
|
component: TextInput,
|
||||||
|
decorators: [ComponentDecorator],
|
||||||
|
args: { placeholder: 'Tim' },
|
||||||
|
render: Render,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default meta;
|
||||||
|
type Story = StoryObj<typeof TextInput>;
|
||||||
|
|
||||||
|
export const Default: Story = {};
|
||||||
|
|
||||||
|
export const Filled: Story = {
|
||||||
|
args: { value: 'Tim' },
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Disabled: Story = {
|
||||||
|
args: { disabled: true, value: 'Tim' },
|
||||||
|
};
|
||||||
@ -1,9 +1,15 @@
|
|||||||
|
import { useState } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
import { SaveAndCancelButtons } from '@/settings/components/SaveAndCancelButtons/SaveAndCancelButtons';
|
import { SaveAndCancelButtons } from '@/settings/components/SaveAndCancelButtons/SaveAndCancelButtons';
|
||||||
import { SettingsHeaderContainer } from '@/settings/components/SettingsHeaderContainer';
|
import { SettingsHeaderContainer } from '@/settings/components/SettingsHeaderContainer';
|
||||||
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
|
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
|
||||||
import { SettingsNewObjectType } from '@/settings/data-model/new-object/components/SettingsNewObjectType';
|
import { SettingsObjectFormSection } from '@/settings/data-model/components/SettingsObjectFormSection';
|
||||||
|
import {
|
||||||
|
NewObjectType,
|
||||||
|
SettingsNewObjectType,
|
||||||
|
} from '@/settings/data-model/new-object/components/SettingsNewObjectType';
|
||||||
|
import { SettingsObjectIconSection } from '@/settings/data-model/object-edit/SettingsObjectIconSection';
|
||||||
import { IconSettings } from '@/ui/display/icon';
|
import { IconSettings } from '@/ui/display/icon';
|
||||||
import { H2Title } from '@/ui/display/typography/components/H2Title';
|
import { H2Title } from '@/ui/display/typography/components/H2Title';
|
||||||
import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer';
|
import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer';
|
||||||
@ -12,6 +18,22 @@ import { Breadcrumb } from '@/ui/navigation/bread-crumb/components/Breadcrumb';
|
|||||||
|
|
||||||
export const SettingsNewObject = () => {
|
export const SettingsNewObject = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const [selectedObjectType, setSelectedObjectType] =
|
||||||
|
useState<NewObjectType>('Standard');
|
||||||
|
|
||||||
|
const [customFormValues, setCustomFormValues] = useState<
|
||||||
|
Partial<{
|
||||||
|
pluralName: string;
|
||||||
|
singularName: string;
|
||||||
|
description: string;
|
||||||
|
}>
|
||||||
|
>({});
|
||||||
|
|
||||||
|
const canSave =
|
||||||
|
selectedObjectType === 'Custom' &&
|
||||||
|
!!customFormValues.pluralName &&
|
||||||
|
!!customFormValues.singularName;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SubMenuTopBarContainer Icon={IconSettings} title="Settings">
|
<SubMenuTopBarContainer Icon={IconSettings} title="Settings">
|
||||||
<SettingsPageContainer>
|
<SettingsPageContainer>
|
||||||
@ -23,11 +45,11 @@ export const SettingsNewObject = () => {
|
|||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
<SaveAndCancelButtons
|
<SaveAndCancelButtons
|
||||||
isSaveDisabled
|
isSaveDisabled={!canSave}
|
||||||
onCancel={() => {
|
onCancel={() => {
|
||||||
navigate('/settings/objects');
|
navigate('/settings/objects');
|
||||||
}}
|
}}
|
||||||
onSave={() => {}}
|
onSave={() => undefined}
|
||||||
/>
|
/>
|
||||||
</SettingsHeaderContainer>
|
</SettingsHeaderContainer>
|
||||||
<Section>
|
<Section>
|
||||||
@ -35,8 +57,27 @@ export const SettingsNewObject = () => {
|
|||||||
title="Object Type"
|
title="Object Type"
|
||||||
description="The type of object you want to add"
|
description="The type of object you want to add"
|
||||||
/>
|
/>
|
||||||
<SettingsNewObjectType />
|
<SettingsNewObjectType
|
||||||
|
selectedType={selectedObjectType}
|
||||||
|
onTypeSelect={setSelectedObjectType}
|
||||||
|
/>
|
||||||
</Section>
|
</Section>
|
||||||
|
{selectedObjectType === 'Custom' && (
|
||||||
|
<>
|
||||||
|
<SettingsObjectIconSection label={customFormValues.pluralName} />
|
||||||
|
<SettingsObjectFormSection
|
||||||
|
singularName={customFormValues.singularName}
|
||||||
|
pluralName={customFormValues.pluralName}
|
||||||
|
description={customFormValues.description}
|
||||||
|
onChange={(formValues) => {
|
||||||
|
setCustomFormValues((previousValues) => ({
|
||||||
|
...previousValues,
|
||||||
|
...formValues,
|
||||||
|
}));
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</SettingsPageContainer>
|
</SettingsPageContainer>
|
||||||
</SubMenuTopBarContainer>
|
</SubMenuTopBarContainer>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -40,10 +40,13 @@ export const SettingsObjectEdit = () => {
|
|||||||
{activeObject && (
|
{activeObject && (
|
||||||
<>
|
<>
|
||||||
<SettingsObjectIconSection
|
<SettingsObjectIconSection
|
||||||
|
disabled={activeObject.type === 'standard'}
|
||||||
Icon={activeObject.Icon}
|
Icon={activeObject.Icon}
|
||||||
iconKey={activeObject.Icon.name}
|
iconKey={activeObject.Icon.name}
|
||||||
|
label={activeObject.name}
|
||||||
/>
|
/>
|
||||||
<SettingsObjectFormSection
|
<SettingsObjectFormSection
|
||||||
|
disabled={activeObject.type === 'standard'}
|
||||||
singularName={activeObject.singularName}
|
singularName={activeObject.singularName}
|
||||||
pluralName={activeObject.name}
|
pluralName={activeObject.name}
|
||||||
description={activeObject.description}
|
description={activeObject.description}
|
||||||
@ -51,12 +54,12 @@ export const SettingsObjectEdit = () => {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<Section>
|
<Section>
|
||||||
<H2Title title="Danger zone" description={`Disable object`} />
|
<H2Title title="Danger zone" description="Disable object" />
|
||||||
<Button
|
<Button
|
||||||
Icon={IconArchive}
|
Icon={IconArchive}
|
||||||
title="Disable"
|
title="Disable"
|
||||||
size="small"
|
size="small"
|
||||||
onClick={() => {}}
|
onClick={() => undefined}
|
||||||
/>
|
/>
|
||||||
</Section>
|
</Section>
|
||||||
</SettingsPageContainer>
|
</SettingsPageContainer>
|
||||||
|
|||||||
Reference in New Issue
Block a user