Skip to content

Commit

Permalink
MDS-716 TagsInput (#2425)
Browse files Browse the repository at this point in the history
* feat: a new example page for the TabsInput component has been created

* feat: a new TagsInput component has been created

* feat: data can be selected and cleared in the TagsInput element

* fix: the callback functions were optimized

* fix: the functionality of the component has been restored

* feat: the TagsInput component and its demo page are completed

* feat: several tests have been added

* cleanup

* bump snapshots

* cleanup

* cleanup

* fix max-width

* fix: corrections in recent remarks
  • Loading branch information
karl-kallavus authored Oct 11, 2023
1 parent 33e0194 commit 76c5ac0
Show file tree
Hide file tree
Showing 21 changed files with 2,960 additions and 4 deletions.
4 changes: 4 additions & 0 deletions next-docs/components/components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,10 @@ const components = [
name: 'Tag',
text: 'Tags represent a set of interactive keywords that help organise and categorise objects.',
},
{
name: 'TagsInput',
text: 'TagsInput is an extension of the text input fields. This component allows users to both enter text and capture input results and display them as well.',
},
{
name: 'Textarea',
text: 'A form control for editing multi-line text.',
Expand Down
170 changes: 170 additions & 0 deletions next-docs/pages/components/tagsInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
import Preview from '../../components/codePreview/Preview';
import ComponentPageDescription from '../../components/ComponentPageDescription';
import type { ComponentNames } from '../../components/getComponent';
import Layout from '../../components/Layout';
import PropsTable from '../../components/PropsTable';
import Default from '../../public/examples/tagsInput/Default';
import LowerCase from '../../public/examples/tagsInput/LowerCase';
import Sizes from '../../public/examples/tagsInput/Sizes';
import States from '../../public/examples/tagsInput/States';
import useComponent from '../../utils/useComponent';

const COMPONENT_NAME: ComponentNames = 'TagsInput';

const PageTagsInput = () => {
const { examples, name, text, image } = useComponent(COMPONENT_NAME);

return (
<>
<ComponentPageDescription
title={name}
image={image}
isAriaSupport
isRtlSupport
isInProgress
>
<p>{text}</p>
<p>
These selected text entries are being dispalyed as tags. Tags represent a set of interactive keywords that help organise and categorise objects.
</p>
<p>
Tags can be added by pressing the Enter key or removed by the mouse click from the input element.
</p>
</ComponentPageDescription>

<Preview
title="Default"
preview={<Default />}
code={examples ? examples.Default : 'Loading'}
/>

<Preview
title="Different sizes"
preview={<Sizes />}
code={examples ? examples.Sizes : 'Loading'}
/>

<Preview
title="States"
preview={<States />}
code={examples ? examples.States : 'Loading'}
/>

<Preview
title="Uppercase & lowercase"
preview={<LowerCase />}
code={examples ? examples.LowerCase : 'Loading'}
/>

<PropsTable
title="TagsInput props"
data={[
{
name: 'selected',
type: 'string[]',
required: true,
default: '-',
description: 'The selected dataset',
},
{
name: 'label',
type: 'string | undefined',
required: false,
default: '-',
description: 'Label title',
},
{
name: 'size',
type: 'sm | md | lg',
required: false,
default: 'md',
description: 'Input size',
},
{
name: 'type',
type: 'date | datetime-local | email | number | password | search | tel | text | time | url | string',
required: false,
default: 'text',
description: 'Input type',
},
{
name: 'placeholder',
type: 'string',
required: false,
default: '-',
description: 'Placeholder for input',
},
{
name: 'isError',
type: 'boolean',
required: false,
default: 'false',
description: 'Sets error state for input',
},
{
name: 'disabled',
type: 'boolean',
required: false,
default: 'false',
description: 'Set disabled/non-disabled',
},
{
name: 'className',
type: 'string',
required: false,
default: '-',
description: 'Tailwind classes for customization',
},
{
name: 'onEnter',
type: '(value: string) => void;',
required: false,
default: '-',
description: 'The function to select the text and append it to the tag set.',
},
{
name: 'onClear',
type: '(index: number) => void',
required: false,
default: '-',
description:
'The function to remove the selected tag.',
},
]}
/>

<PropsTable
title="TagsInput.SelectedItem"
data={[
{
name: 'className',
type: 'string',
required: false,
default: '-',
description: 'Tailwind classes for customization',
},
{
name: 'index',
type: 'number',
required: true,
default: '-',
description: 'This data spcifies the key value of the item',
},
{
name: 'label',
type: 'string',
required: true,
default: '-',
description: 'The text selected as tag',
},
]}
/>
</>
);
};

PageTagsInput.getLayout = function getLayout(page: React.ReactNode) {
return <Layout title={`Components | ${COMPONENT_NAME}`}>{page}</Layout>;
};

export default PageTagsInput;
Binary file added next-docs/public/components/tagsinput.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
30 changes: 30 additions & 0 deletions next-docs/public/examples/tagsInput/Default.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { TagsInput } from "@heathmont/moon-core-tw";
import { useCallback, useState } from "react";

const Example = () => {
const [selected, setSelected] = useState<string[]>([]);

const onEnter = useCallback((value: string) => {
setSelected([...selected, value]);
}, [selected, setSelected]);

const onClear = useCallback((index: number) => {
setSelected(selected.filter((item, id) => id !== index));
}, [selected, setSelected]);

return (
<div className="flex flex-col lg:flex-row lg:justify-center items-center w-full max-w-xs gap-4">
<TagsInput
selected={selected}
onEnter={onEnter}
onClear={onClear}
>
{selected.map((text, index) => (
<TagsInput.SelectedItem index={index} label={text} />
))}
</TagsInput>
</div>
);
};

export default Example;
57 changes: 57 additions & 0 deletions next-docs/public/examples/tagsInput/LowerCase.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { TagsInput } from "@heathmont/moon-core-tw";
import { useCallback, useState } from "react";

const Example = () => {
const [selected1, setSelected1] = useState<string[]>(['Preset data']);
const [selected2, setSelected2] = useState<string[]>(['Preset data']);

const onEnter1 = useCallback((value: string) => {
setSelected1([...selected1, value]);
}, [selected1, setSelected1]);

const onEnter2 = useCallback((value: string) => {
setSelected2([...selected2, value]);
}, [selected2, setSelected2]);

const onClear1 = useCallback((index: number) => {
setSelected1(selected1.filter((item, id) => id !== index));
}, [selected1, setSelected1]);

const onClear2 = useCallback((index: number) => {
setSelected2(selected2.filter((item, id) => id !== index));
}, [selected2, setSelected2]);

return (
<div className="flex flex-col items-center w-full h-50">
<div className="flex flex-col items-center lg:flex-row lg:justify-center lg:items-start w-full gap-4">
<div className="flex flex-col w-full max-w-xs">
<TagsInput
selected={selected1}
label="Lower"
onEnter={onEnter1}
onClear={onClear1}
>
{selected1.map((text, index) => (
<TagsInput.SelectedItem isUppercase={false} index={index} label={text} />
))}
</TagsInput>
</div>

<div className="flex flex-col w-full max-w-xs">
<TagsInput
selected={selected2}
label="Capitalized"
onEnter={onEnter2}
onClear={onClear2}
>
{selected2.map((text, index) => (
<TagsInput.SelectedItem index={index} label={text} />
))}
</TagsInput>
</div>
</div>
</div>
);
};

export default Example;
81 changes: 81 additions & 0 deletions next-docs/public/examples/tagsInput/Sizes.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { TagsInput } from "@heathmont/moon-core-tw";
import { useCallback, useState } from "react";

const Example = () => {
const [selected0, setSelected0] = useState<string[]>([]);
const [selected1, setSelected1] = useState<string[]>([]);
const [selected2, setSelected2] = useState<string[]>([]);

const onEnter0 = useCallback((value: string) => {
setSelected0([...selected0, value]);
}, [selected0, setSelected0]);

const onEnter1 = useCallback((value: string) => {
setSelected1([...selected1, value]);
}, [selected1, setSelected1]);

const onEnter2 = useCallback((value: string) => {
setSelected2([...selected2, value]);
}, [selected2, setSelected2]);

const onClear0 = useCallback((index: number) => {
setSelected0(selected0.filter((item, id) => id !== index));
}, [selected0, setSelected0]);

const onClear1 = useCallback((index: number) => {
setSelected1(selected1.filter((item, id) => id !== index));
}, [selected1, setSelected1]);

const onClear2 = useCallback((index: number) => {
setSelected2(selected2.filter((item, id) => id !== index));
}, [selected2, setSelected2]);

return (
<div className="flex flex-col items-center w-full h-50">
<div className="flex flex-col items-center lg:flex-row lg:justify-center lg:items-end w-full gap-4">
<div className="flex flex-col w-full max-w-xs">
<TagsInput
selected={selected0}
label="Small"
onEnter={onEnter0}
onClear={onClear0}
size="sm"
>
{selected0.map((text, index) => (
<TagsInput.SelectedItem index={index} label={text} />
))}
</TagsInput>
</div>

<div className="flex flex-col w-full max-w-xs">
<TagsInput
selected={selected1}
label="Medium (default)"
onEnter={onEnter1}
onClear={onClear1}
>
{selected1.map((text, index) => (
<TagsInput.SelectedItem index={index} label={text} />
))}
</TagsInput>
</div>

<div className="flex flex-col w-full max-w-xs">
<TagsInput
selected={selected2}
label="Large"
onEnter={onEnter2}
onClear={onClear2}
size="lg"
>
{selected2.map((text, index) => (
<TagsInput.SelectedItem index={index} label={text} />
))}
</TagsInput>
</div>
</div>
</div>
);
};

export default Example;
Loading

0 comments on commit 76c5ac0

Please sign in to comment.