From 25ccef7578a67906f5918f7245c40074d6c0ffc5 Mon Sep 17 00:00:00 2001 From: Arlindo Pereira Date: Fri, 15 Nov 2024 12:48:27 +0100 Subject: [PATCH 01/19] Tag control --- src/components/forms/controls/Radio/Radio.tsx | 2 +- .../forms/controls/Tag/Tag.module.scss | 27 +++++++++++ .../forms/controls/Tag/Tag.stories.tsx | 32 +++++++++++++ src/components/forms/controls/Tag/Tag.tsx | 46 +++++++++++++++++++ 4 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 src/components/forms/controls/Tag/Tag.module.scss create mode 100644 src/components/forms/controls/Tag/Tag.stories.tsx create mode 100644 src/components/forms/controls/Tag/Tag.tsx diff --git a/src/components/forms/controls/Radio/Radio.tsx b/src/components/forms/controls/Radio/Radio.tsx index d086d83..6a04e93 100644 --- a/src/components/forms/controls/Radio/Radio.tsx +++ b/src/components/forms/controls/Radio/Radio.tsx @@ -15,7 +15,7 @@ export type RadioProps = ComponentProps<'input'> & { unstyled?: undefined | boolean, }; /** - * A simple Radio control, just the <input type="radio"> and nothing else.. + * A simple Radio control, just the <input type="radio"> and nothing else. */ export const Radio = (props: RadioProps) => { const { diff --git a/src/components/forms/controls/Tag/Tag.module.scss b/src/components/forms/controls/Tag/Tag.module.scss new file mode 100644 index 0000000..915f144 --- /dev/null +++ b/src/components/forms/controls/Tag/Tag.module.scss @@ -0,0 +1,27 @@ +/* Copyright (c) Fortanix, Inc. +|* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of +|* the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +@use '../../../../styling/defs.scss' as bk; + +@layer baklava.components { + .bk-tag { + @include bk.component-base(bk-tag); + + background: bk.$theme-tag-background-default; + border-radius: 2px; + color: bk.$theme-tag-text-default; + display: flex; + align-items: center; + font-size: bk.$font-size-xs; + padding: 2px 0 3px bk.$spacing-2; + + .bk-tag__icon { + width: 7px; + height: 7px; + color: bk.$theme-tag-icon-default; + cursor: pointer; + padding: 2px bk.$spacing-2 2px bk.$spacing-2; + } + } +} diff --git a/src/components/forms/controls/Tag/Tag.stories.tsx b/src/components/forms/controls/Tag/Tag.stories.tsx new file mode 100644 index 0000000..e6e7d44 --- /dev/null +++ b/src/components/forms/controls/Tag/Tag.stories.tsx @@ -0,0 +1,32 @@ +/* Copyright (c) Fortanix, Inc. +|* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of +|* the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +import type { Meta, StoryObj } from '@storybook/react'; + +import * as React from 'react'; + +import { Tag } from './Tag.tsx'; + + +type TagArgs = React.ComponentProps; +type Story = StoryObj; + +export default { + component: Tag, + parameters: { + layout: 'centered', + }, + tags: ['autodocs'], + argTypes: { + }, + args: { + value: 'Tag Title', + }, + render: (args) => , +} satisfies Meta; + + +export const TagStory: Story = { + name: 'Tag', +}; diff --git a/src/components/forms/controls/Tag/Tag.tsx b/src/components/forms/controls/Tag/Tag.tsx new file mode 100644 index 0000000..8848081 --- /dev/null +++ b/src/components/forms/controls/Tag/Tag.tsx @@ -0,0 +1,46 @@ +/* Copyright (c) Fortanix, Inc. +|* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of +|* the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +import { classNames as cx, type ComponentProps } from '../../../../util/componentUtil.ts'; +import * as React from 'react'; + +import { Icon } from '../../../graphics/Icon/Icon.tsx'; + +import cl from './Tag.module.scss'; + + +export { cl as TagClassNames }; + +export type TagProps = ComponentProps<'div'> & { + /** Whether this component should be unstyled. */ + unstyled?: undefined | boolean, + + /** The text displayed inside the tag. */ + value: string, +}; + +/** + * A tag component, meant to be used within Input fields. + */ +export const Tag = (props: TagProps) => { + const { + unstyled = false, + value = '', + ...propsRest + } = props; + + return ( +
+ {value} + +
+ ); +}; From ea2c70e3cb3d568f3e4652b5307394c93fcd895b Mon Sep 17 00:00:00 2001 From: Arlindo Pereira Date: Tue, 19 Nov 2024 15:01:13 +0100 Subject: [PATCH 02/19] InputField story using Tags --- .../fields/InputField/InputField.module.scss | 24 +++++++++-- .../fields/InputField/InputField.stories.tsx | 29 ++++++++++++++ .../forms/fields/InputField/InputField.tsx | 40 +++++++++++++------ 3 files changed, 77 insertions(+), 16 deletions(-) diff --git a/src/components/forms/fields/InputField/InputField.module.scss b/src/components/forms/fields/InputField/InputField.module.scss index 490a36a..57267c3 100644 --- a/src/components/forms/fields/InputField/InputField.module.scss +++ b/src/components/forms/fields/InputField/InputField.module.scss @@ -7,18 +7,36 @@ @layer baklava.components { .bk-input-field { @include bk.component-base(bk-input-field); - + display: flex; flex-direction: column; gap: 6px; - + .bk-input-field__label { @include bk.font(bk.$font-family-body, bk.$font-weight-semibold); cursor: default; } - + .bk-input-field__control { --empty: ; // Prevent empty class from being removed } } + + .bk-input-field__tags-and-input { + display: flex; + flex-direction: row; + gap: bk.$spacing-2; + } + + .bk-input-field--with-tags { + border-bottom: 1px solid bk.$theme-form-rule-default; + + &:focus-within { + border-bottom-color: bk.$theme-form-rule-focused; + + input { + outline: none !important; + } + } + } } diff --git a/src/components/forms/fields/InputField/InputField.stories.tsx b/src/components/forms/fields/InputField/InputField.stories.tsx index 8825777..d832b73 100644 --- a/src/components/forms/fields/InputField/InputField.stories.tsx +++ b/src/components/forms/fields/InputField/InputField.stories.tsx @@ -55,3 +55,32 @@ export const InvalidInput: Story = { await fireEvent.submit(input.closest('form')!); }, }; + +export const InputWithTags: Story = { + name: 'Input with tags (enter creates new tag, backspace erases tags)', + render: () => { + const onChange = (e: React.ChangeEvent) => { + setInputText(e.target.value); + }; + const onKeyUp = (e: React.KeyboardEvent) => { + if (e.key === 'Backspace' && inputText === '') { + setTags(tags.slice(0,-1)); + } + if (e.key === 'Enter' && inputText !== '') { + setTags([...tags, inputText]); + setInputText(''); + } + }; + const [tags, setTags] = React.useState>(['Tag Title', 'Tag Title 2']); + const [inputText, setInputText] = React.useState('Example'); + return ( + + ); + } +}; diff --git a/src/components/forms/fields/InputField/InputField.tsx b/src/components/forms/fields/InputField/InputField.tsx index 3e8805c..0ea34a1 100644 --- a/src/components/forms/fields/InputField/InputField.tsx +++ b/src/components/forms/fields/InputField/InputField.tsx @@ -4,10 +4,10 @@ import { classNames as cx, type ComponentProps } from '../../../../util/componentUtil.ts'; import * as React from 'react'; -import { useFormStatus } from 'react-dom'; import { useFormContext } from '../../context/Form/Form.tsx'; import { Input } from '../../controls/Input/Input.tsx'; +import { Tag } from '../../controls/Tag/Tag.tsx'; import cl from './InputField.module.scss'; @@ -17,15 +17,18 @@ export { cl as InputFieldClassNames }; export type InputFieldProps = ComponentProps<'input'> & { /** Whether this component should be unstyled. */ unstyled?: undefined | boolean, - + /** Label for the input. */ label?: undefined | React.ReactNode, - + /** Props for the `