Merge pull request #19366 from overleaf/ii-bs5-projects-list-search

[web] BS5 projects list search

GitOrigin-RevId: e2545f43ac3a50e58f7e97a2038e5b768c909e4f
This commit is contained in:
ilkin-overleaf 2024-07-16 13:40:21 +03:00 committed by Copybot
parent f54b257022
commit 0a23c55c93
15 changed files with 455 additions and 191 deletions

View file

@ -102,7 +102,7 @@ export default function CloneProjectModalContent({
{clonedProjectTags.length > 0 && (
<OLFormGroup
controlId="clone-project-tags-list"
className="clone-project-tag mb-3"
className="clone-project-tag"
>
<OLFormLabel>{t('tags')}: </OLFormLabel>
<div role="listbox" id="clone-project-tags-list">

View file

@ -97,7 +97,6 @@ function ProjectListPageContent() {
filter={filter}
selectedTag={selectedTag}
className="overflow-hidden"
formGroupProps={{ className: 'mb-0' }}
/>
</div>
</div>

View file

@ -1,36 +1,38 @@
import { useTranslation } from 'react-i18next'
import {
Form,
FormGroup,
FormGroupProps,
Col,
FormControl,
} from 'react-bootstrap'
import { FormControl } from 'react-bootstrap'
import Icon from '../../../shared/components/icon'
import * as eventTracking from '../../../infrastructure/event-tracking'
import classnames from 'classnames'
import { Tag } from '../../../../../app/src/Features/Tags/types'
import { MergeAndOverride } from '../../../../../types/utils'
import { Filter } from '../context/project-list-context'
import { isSmallDevice } from '../../../infrastructure/event-tracking'
import OLForm from '@/features/ui/components/ol/ol-form'
import OLFormGroup from '@/features/ui/components/ol/ol-form-group'
import OLCol from '@/features/ui/components/ol/ol-col'
import OLFormControl from '@/features/ui/components/ol/ol-form-control'
import MaterialIcon from '@/shared/components/material-icon'
import BootstrapVersionSwitcher from '@/features/ui/components/bootstrap-5/bootstrap-version-switcher'
import { bsVersion } from '@/features/utils/bootstrap-5'
type SearchFormOwnProps = {
inputValue: string
setInputValue: (input: string) => void
filter: Filter
selectedTag: Tag | undefined
formGroupProps?: FormGroupProps &
Omit<React.ComponentProps<'div'>, keyof FormGroupProps>
}
type SearchFormProps = SearchFormOwnProps &
Omit<React.ComponentProps<typeof Form>, keyof SearchFormOwnProps>
type SearchFormProps = MergeAndOverride<
React.ComponentProps<typeof OLForm>,
SearchFormOwnProps
>
function SearchForm({
inputValue,
setInputValue,
filter,
selectedTag,
formGroupProps,
className,
...props
}: SearchFormProps) {
const { t } = useTranslation()
@ -57,8 +59,6 @@ function SearchForm({
}
}
const placeholder = `${placeholderMessage}`
const { className: formGroupClassName, ...restFormGroupProps } =
formGroupProps || {}
const handleChange = (
e: React.ChangeEvent<
@ -75,44 +75,49 @@ function SearchForm({
const handleClear = () => setInputValue('')
return (
<Form
horizontal
className="project-search"
<OLForm
className={classnames('project-search', className)}
role="search"
onSubmit={e => e.preventDefault()}
bs3Props={{ horizontal: true }}
{...props}
>
<FormGroup
className={classnames(
'has-feedback has-feedback-left',
formGroupClassName
)}
{...restFormGroupProps}
<OLFormGroup
className={bsVersion({ bs3: 'has-feedback has-feedback-left' })}
>
<Col xs={12}>
<FormControl
<OLCol>
<OLFormControl
type="text"
value={inputValue}
onChange={handleChange}
placeholder={placeholder}
aria-label={placeholder}
/>
<Icon type="search" className="form-control-feedback-left" />
{inputValue.length ? (
<div className="form-control-feedback">
prepend={BootstrapVersionSwitcher({
bs3: <Icon type="search" />,
bs5: <MaterialIcon type="search" />,
})}
append={
inputValue.length > 0 && (
<button
type="button"
className="project-search-clear-btn btn-link"
className={bsVersion({
bs3: 'project-search-clear-btn btn-link',
bs5: 'project-search-clear-btn',
})}
aria-label={t('clear_search')}
onClick={handleClear}
>
<Icon type="times" />
<BootstrapVersionSwitcher
bs3={<Icon type="times" />}
bs5={<MaterialIcon type="clear" />}
/>
</button>
</div>
) : null}
</Col>
</FormGroup>
</Form>
)
}
/>
</OLCol>
</OLFormGroup>
</OLForm>
)
}

View file

@ -0,0 +1,26 @@
import {
FormControl as BS3FormControl,
FormControlProps as BS3FormControlProps,
} from 'react-bootstrap'
type FormControlProps = BS3FormControlProps & {
prepend?: React.ReactNode
append?: React.ReactNode
}
function FormControl({
prepend,
append,
className,
...props
}: FormControlProps) {
return (
<>
{prepend && <div className="form-control-feedback-left">{prepend}</div>}
<BS3FormControl {...props} />
{append && <div className="form-control-feedback">{append}</div>}
</>
)
}
export default FormControl

View file

@ -0,0 +1,44 @@
import { forwardRef } from 'react'
import { Form, FormControlProps } from 'react-bootstrap-5'
import classnames from 'classnames'
type OLFormControlProps = FormControlProps & {
prepend?: React.ReactNode
append?: React.ReactNode
}
const FormControl = forwardRef<HTMLInputElement, OLFormControlProps>(
({ prepend, append, className, ...props }, ref) => {
if (prepend || append) {
const wrapperClassNames = classnames('form-control-wrapper', {
'form-control-wrapper-sm': props.size === 'sm',
'form-control-wrapper-lg': props.size === 'lg',
'form-control-wrapper-disabled': props.disabled,
})
const formControlClassNames = classnames(className, {
'form-control-offset-start': prepend,
'form-control-offset-end': append,
})
return (
<div className={wrapperClassNames}>
{prepend && (
<span className="form-control-start-icon">{prepend}</span>
)}
<Form.Control
{...props}
className={formControlClassNames}
ref={ref}
/>
{append && <span className="form-control-end-icon">{append}</span>}
</div>
)
}
return <Form.Control {...props} />
}
)
FormControl.displayName = 'FormControl'
export default FormControl

View file

@ -0,0 +1,14 @@
import { forwardRef } from 'react'
import { FormGroup as BS5FormGroup, FormGroupProps } from 'react-bootstrap-5'
import classnames from 'classnames'
const FormGroup = forwardRef<typeof BS5FormGroup, FormGroupProps>(
({ className, ...props }, ref) => {
const classNames = classnames('form-group', className)
return <BS5FormGroup className={classNames} {...props} ref={ref} />
}
)
FormGroup.displayName = 'FormGroup'
export default FormGroup

View file

@ -1,11 +1,12 @@
import { forwardRef } from 'react'
import { Form } from 'react-bootstrap-5'
import { FormControl as BS3FormControl } from 'react-bootstrap'
import BootstrapVersionSwitcher from '@/features/ui/components/bootstrap-5/bootstrap-version-switcher'
import { getAriaAndDataProps } from '@/features/utils/bootstrap-5'
import BS3FormControl from '@/features/ui/components/bootstrap-3/form/form-control'
import FormControl from '@/features/ui/components/bootstrap-5/form/form-control'
import BootstrapVersionSwitcher from '@/features/ui/components/bootstrap-5/bootstrap-version-switcher'
type OLFormControlProps = React.ComponentProps<(typeof Form)['Control']> & {
type OLFormControlProps = React.ComponentProps<typeof FormControl> & {
bs3Props?: Record<string, unknown>
'data-ol-dirty'?: unknown
}
const OLFormControl = forwardRef<HTMLInputElement, OLFormControlProps>(
@ -37,6 +38,8 @@ const OLFormControl = forwardRef<HTMLInputElement, OLFormControlProps>(
ref.current = inputElement
}
},
prepend: rest.prepend,
append: rest.append,
...bs3Props,
}
@ -49,7 +52,7 @@ const OLFormControl = forwardRef<HTMLInputElement, OLFormControlProps>(
return (
<BootstrapVersionSwitcher
bs3={<BS3FormControl {...bs3FormControlProps} />}
bs5={<Form.Control ref={ref} {...rest} />}
bs5={<FormControl ref={ref} {...rest} />}
/>
)
}

View file

@ -1,27 +1,26 @@
import { Form } from 'react-bootstrap-5'
import { FormGroupProps } from 'react-bootstrap-5'
import { FormGroup as BS3FormGroup } from 'react-bootstrap'
import FormGroup from '@/features/ui/components/bootstrap-5/form/form-group'
import BootstrapVersionSwitcher from '@/features/ui/components/bootstrap-5/bootstrap-version-switcher'
type OLFormGroupProps = React.ComponentProps<(typeof Form)['Group']> & {
type OLFormGroupProps = FormGroupProps & {
bs3Props?: Record<string, unknown>
}
function OLFormGroup(props: OLFormGroupProps) {
const { bs3Props, className, ...rest } = props
const classNames = className ?? 'mb-3'
const { bs3Props, ...rest } = props
const bs3FormGroupProps: React.ComponentProps<typeof BS3FormGroup> = {
children: rest.children,
controlId: rest.controlId,
className,
className: rest.className,
...bs3Props,
}
return (
<BootstrapVersionSwitcher
bs3={<BS3FormGroup {...bs3FormGroupProps} />}
bs5={<Form.Group className={classNames} {...rest} />}
bs5={<FormGroup {...rest} />}
/>
)
}

View file

@ -11,10 +11,11 @@ function OLForm(props: OLFormProps) {
const bs3FormProps: React.ComponentProps<typeof BS3Form> = {
componentClass: rest.as,
bsClass: rest.className,
children: rest.children,
id: rest.id,
onSubmit: rest.onSubmit as React.FormEventHandler<BS3Form> | undefined,
className: rest.className,
role: rest.role,
...bs3Props,
}

View file

@ -1,39 +1,42 @@
import { Form } from 'react-bootstrap-5'
import type { Meta, StoryObj } from '@storybook/react'
import FormGroup from '@/features/ui/components/bootstrap-5/form/form-group'
import FormText from '@/features/ui/components/bootstrap-5/form/form-text'
import FormControl from '@/features/ui/components/bootstrap-5/form/form-control'
import MaterialIcon from '@/shared/components/material-icon'
const meta: Meta<(typeof Form)['Control']> = {
const meta: Meta<React.ComponentProps<typeof FormControl>> = {
title: 'Shared / Components / Bootstrap 5 / Form / Input',
component: Form.Control,
component: FormControl,
parameters: {
bootstrap5: true,
},
}
export default meta
type Story = StoryObj<(typeof Form)['Control']>
type Story = StoryObj<React.ComponentProps<typeof FormControl>>
export const Default: Story = {
render: args => {
return (
<>
<Form.Group controlId="id-1">
<FormGroup controlId="id-1">
<Form.Label>Label</Form.Label>
<Form.Control defaultValue="Large input" size="lg" {...args} />
<FormControl defaultValue="Large input" size="lg" {...args} />
<FormText>Helper</FormText>
</Form.Group>
</FormGroup>
<hr />
<Form.Group controlId="id-2">
<FormGroup controlId="id-2">
<Form.Label>Label</Form.Label>
<Form.Control defaultValue="Regular input" {...args} />
<FormControl defaultValue="Regular input" {...args} />
<FormText>Helper</FormText>
</Form.Group>
</FormGroup>
<hr />
<Form.Group controlId="id-3">
<FormGroup controlId="id-3">
<Form.Label>Label</Form.Label>
<Form.Control defaultValue="Small input" size="sm" {...args} />
<FormControl defaultValue="Small input" size="sm" {...args} />
<FormText>Helper</FormText>
</Form.Group>
</FormGroup>
</>
)
},
@ -46,37 +49,37 @@ export const Info: Story = {
render: args => {
return (
<>
<Form.Group controlId="id-1">
<FormGroup controlId="id-1">
<Form.Label>Label</Form.Label>
<Form.Control
<FormControl
placeholder="Placeholder"
defaultValue="Large input"
size="lg"
{...args}
/>
<FormText isInfo>Info</FormText>
</Form.Group>
</FormGroup>
<hr />
<Form.Group controlId="id-2">
<FormGroup controlId="id-2">
<Form.Label>Label</Form.Label>
<Form.Control
<FormControl
placeholder="Placeholder"
defaultValue="Regular input"
{...args}
/>
<FormText isInfo>Info</FormText>
</Form.Group>
</FormGroup>
<hr />
<Form.Group controlId="id-3">
<FormGroup controlId="id-3">
<Form.Label>Label</Form.Label>
<Form.Control
<FormControl
placeholder="Placeholder"
defaultValue="Small input"
size="sm"
{...args}
/>
<FormText isInfo>Info</FormText>
</Form.Group>
</FormGroup>
</>
)
},
@ -86,9 +89,9 @@ export const Error: Story = {
render: args => {
return (
<>
<Form.Group controlId="id-1">
<FormGroup controlId="id-1">
<Form.Label>Label</Form.Label>
<Form.Control
<FormControl
placeholder="Placeholder"
defaultValue="Large input"
size="lg"
@ -96,22 +99,22 @@ export const Error: Story = {
{...args}
/>
<FormText isError>Error</FormText>
</Form.Group>
</FormGroup>
<hr />
<Form.Group controlId="id-2">
<FormGroup controlId="id-2">
<Form.Label>Label</Form.Label>
<Form.Control
<FormControl
placeholder="Placeholder"
defaultValue="Regular input"
isInvalid
{...args}
/>
<FormText isError>Error</FormText>
</Form.Group>
</FormGroup>
<hr />
<Form.Group controlId="id-3">
<FormGroup controlId="id-3">
<Form.Label>Label</Form.Label>
<Form.Control
<FormControl
placeholder="Placeholder"
defaultValue="Small input"
size="sm"
@ -119,7 +122,7 @@ export const Error: Story = {
{...args}
/>
<FormText isError>Error</FormText>
</Form.Group>
</FormGroup>
</>
)
},
@ -129,37 +132,37 @@ export const Warning: Story = {
render: args => {
return (
<>
<Form.Group controlId="id-1">
<FormGroup controlId="id-1">
<Form.Label>Label</Form.Label>
<Form.Control
<FormControl
placeholder="Placeholder"
defaultValue="Large input"
size="lg"
{...args}
/>
<FormText isWarning>Warning</FormText>
</Form.Group>
</FormGroup>
<hr />
<Form.Group controlId="id-2">
<FormGroup controlId="id-2">
<Form.Label>Label</Form.Label>
<Form.Control
<FormControl
placeholder="Placeholder"
defaultValue="Regular input"
{...args}
/>
<FormText isWarning>Warning</FormText>
</Form.Group>
</FormGroup>
<hr />
<Form.Group controlId="id-3">
<FormGroup controlId="id-3">
<Form.Label>Label</Form.Label>
<Form.Control
<FormControl
placeholder="Placeholder"
defaultValue="Small input"
size="sm"
{...args}
/>
<FormText isWarning>Warning</FormText>
</Form.Group>
</FormGroup>
</>
)
},
@ -169,37 +172,130 @@ export const Success: Story = {
render: args => {
return (
<>
<Form.Group controlId="id-1">
<FormGroup controlId="id-1">
<Form.Label>Label</Form.Label>
<Form.Control
<FormControl
placeholder="Placeholder"
defaultValue="Large input"
size="lg"
{...args}
/>
<FormText isSuccess>Success</FormText>
</Form.Group>
</FormGroup>
<hr />
<Form.Group controlId="id-2">
<FormGroup controlId="id-2">
<Form.Label>Label</Form.Label>
<Form.Control
<FormControl
placeholder="Placeholder"
defaultValue="Regular input"
{...args}
/>
<FormText isSuccess>Success</FormText>
</Form.Group>
</FormGroup>
<hr />
<Form.Group controlId="id-3">
<FormGroup controlId="id-3">
<Form.Label>Label</Form.Label>
<Form.Control
<FormControl
placeholder="Placeholder"
defaultValue="Small input"
size="sm"
{...args}
/>
<FormText isSuccess>Success</FormText>
</Form.Group>
</FormGroup>
</>
)
},
}
export const WithIcons: Story = {
render: args => {
const handleClear = () => {
alert('Clicked clear button')
}
return (
<>
<FormGroup controlId="id-1">
<Form.Label>Label</Form.Label>
<FormControl
type="text"
placeholder="Search"
prepend={<MaterialIcon type="search" />}
append={
<button
type="button"
className="project-search-clear-btn"
onClick={handleClear}
>
<MaterialIcon type="clear" />
</button>
}
size="lg"
{...args}
/>
</FormGroup>
<hr />
<FormGroup controlId="id-2">
<Form.Label>Label</Form.Label>
<FormControl
type="text"
placeholder="Search"
prepend={<MaterialIcon type="search" />}
append={
<button
type="button"
className="project-search-clear-btn"
onClick={handleClear}
>
<MaterialIcon type="clear" />
</button>
}
{...args}
/>
</FormGroup>
<hr />
<FormGroup controlId="id-3">
<Form.Label>Label</Form.Label>
<FormControl
type="text"
placeholder="Search"
prepend={<MaterialIcon type="search" />}
append={
<button
type="button"
className="project-search-clear-btn"
onClick={handleClear}
>
<MaterialIcon type="clear" />
</button>
}
size="sm"
{...args}
/>
</FormGroup>
<br />
<hr />
<FormGroup controlId="id-3">
<Form.Label>Disabled state</Form.Label>
<FormControl
type="text"
placeholder="Search"
prepend={<MaterialIcon type="search" />}
append={
<button
type="button"
className="project-search-clear-btn"
onClick={handleClear}
disabled
>
<MaterialIcon type="clear" />
</button>
}
disabled
{...args}
/>
</FormGroup>
</>
)
},

View file

@ -1,8 +1,9 @@
import { Form } from 'react-bootstrap-5'
import { Form, FormSelectProps } from 'react-bootstrap-5'
import type { Meta, StoryObj } from '@storybook/react'
import FormGroup from '@/features/ui/components/bootstrap-5/form/form-group'
import FormText from '@/features/ui/components/bootstrap-5/form/form-text'
const meta: Meta<(typeof Form)['Select']> = {
const meta: Meta<FormSelectProps> = {
title: 'Shared / Components / Bootstrap 5 / Form / Select',
component: Form.Select,
parameters: {
@ -11,13 +12,13 @@ const meta: Meta<(typeof Form)['Select']> = {
}
export default meta
type Story = StoryObj<(typeof Form)['Select']>
type Story = StoryObj<FormSelectProps>
export const Default: Story = {
render: args => {
return (
<>
<Form.Group controlId="id-1">
<FormGroup controlId="id-1">
<Form.Label>Label</Form.Label>
<Form.Select size="lg" {...args}>
<option>Large select</option>
@ -26,9 +27,9 @@ export const Default: Story = {
<option value="3">Three</option>
</Form.Select>
<FormText>Helper</FormText>
</Form.Group>
</FormGroup>
<hr />
<Form.Group controlId="id-2">
<FormGroup controlId="id-2">
<Form.Label>Label</Form.Label>
<Form.Select {...args}>
<option>Regular select</option>
@ -37,9 +38,9 @@ export const Default: Story = {
<option value="3">Three</option>
</Form.Select>
<FormText>Helper</FormText>
</Form.Group>
</FormGroup>
<hr />
<Form.Group controlId="id-3">
<FormGroup controlId="id-3">
<Form.Label>Label</Form.Label>
<Form.Select size="sm" {...args}>
<option>Small select</option>
@ -48,7 +49,7 @@ export const Default: Story = {
<option value="3">Three</option>
</Form.Select>
<FormText>Helper</FormText>
</Form.Group>
</FormGroup>
</>
)
},
@ -61,7 +62,7 @@ export const Info: Story = {
render: args => {
return (
<>
<Form.Group controlId="id-1">
<FormGroup controlId="id-1">
<Form.Label>Label</Form.Label>
<Form.Select size="lg" {...args}>
<option>Large select</option>
@ -70,9 +71,9 @@ export const Info: Story = {
<option value="3">Three</option>
</Form.Select>
<FormText isInfo>Info</FormText>
</Form.Group>
</FormGroup>
<hr />
<Form.Group controlId="id-2">
<FormGroup controlId="id-2">
<Form.Label>Label</Form.Label>
<Form.Select {...args}>
<option>Regular select</option>
@ -81,9 +82,9 @@ export const Info: Story = {
<option value="3">Three</option>
</Form.Select>
<FormText isInfo>Info</FormText>
</Form.Group>
</FormGroup>
<hr />
<Form.Group controlId="id-3">
<FormGroup controlId="id-3">
<Form.Label>Label</Form.Label>
<Form.Select size="sm" {...args}>
<option>Small select</option>
@ -92,7 +93,7 @@ export const Info: Story = {
<option value="3">Three</option>
</Form.Select>
<FormText isInfo>Info</FormText>
</Form.Group>
</FormGroup>
</>
)
},
@ -102,7 +103,7 @@ export const Error: Story = {
render: args => {
return (
<>
<Form.Group controlId="id-1">
<FormGroup controlId="id-1">
<Form.Label>Label</Form.Label>
<Form.Select size="lg" isInvalid {...args}>
<option>Large select</option>
@ -111,9 +112,9 @@ export const Error: Story = {
<option value="3">Three</option>
</Form.Select>
<FormText isError>Error</FormText>
</Form.Group>
</FormGroup>
<hr />
<Form.Group controlId="id-2">
<FormGroup controlId="id-2">
<Form.Label>Label</Form.Label>
<Form.Select isInvalid {...args}>
<option>Regular select</option>
@ -122,9 +123,9 @@ export const Error: Story = {
<option value="3">Three</option>
</Form.Select>
<FormText isError>Error</FormText>
</Form.Group>
</FormGroup>
<hr />
<Form.Group controlId="id-3">
<FormGroup controlId="id-3">
<Form.Label>Label</Form.Label>
<Form.Select size="sm" isInvalid {...args}>
<option>Small select</option>
@ -133,7 +134,7 @@ export const Error: Story = {
<option value="3">Three</option>
</Form.Select>
<FormText isError>Error</FormText>
</Form.Group>
</FormGroup>
</>
)
},
@ -143,7 +144,7 @@ export const Warning: Story = {
render: args => {
return (
<>
<Form.Group controlId="id-1">
<FormGroup controlId="id-1">
<Form.Label>Label</Form.Label>
<Form.Select size="lg" {...args}>
<option>Large select</option>
@ -152,9 +153,9 @@ export const Warning: Story = {
<option value="3">Three</option>
</Form.Select>
<FormText isWarning>Warning</FormText>
</Form.Group>
</FormGroup>
<hr />
<Form.Group controlId="id-2">
<FormGroup controlId="id-2">
<Form.Label>Label</Form.Label>
<Form.Select {...args}>
<option>Regular select</option>
@ -163,9 +164,9 @@ export const Warning: Story = {
<option value="3">Three</option>
</Form.Select>
<FormText isWarning>Warning</FormText>
</Form.Group>
</FormGroup>
<hr />
<Form.Group controlId="id-3">
<FormGroup controlId="id-3">
<Form.Label>Label</Form.Label>
<Form.Select size="sm" {...args}>
<option>Small select</option>
@ -174,7 +175,7 @@ export const Warning: Story = {
<option value="3">Three</option>
</Form.Select>
<FormText isWarning>Warning</FormText>
</Form.Group>
</FormGroup>
</>
)
},
@ -184,38 +185,38 @@ export const Success: Story = {
render: args => {
return (
<>
<Form.Group controlId="id-1">
<FormGroup controlId="id-1">
<Form.Label>Label</Form.Label>
<Form.Select size="lg" isSuccess {...args}>
<Form.Select size="lg" {...args}>
<option>Large select</option>
<option value="1">One</option>
<option value="2">Two</option>
<option value="3">Three</option>
</Form.Select>
<FormText isSuccess>Success</FormText>
</Form.Group>
</FormGroup>
<hr />
<Form.Group controlId="id-2">
<FormGroup controlId="id-2">
<Form.Label>Label</Form.Label>
<Form.Select isSuccess {...args}>
<Form.Select {...args}>
<option>Regular select</option>
<option value="1">One</option>
<option value="2">Two</option>
<option value="3">Three</option>
</Form.Select>
<FormText isSuccess>Success</FormText>
</Form.Group>
</FormGroup>
<hr />
<Form.Group controlId="id-3">
<FormGroup controlId="id-3">
<Form.Label>Label</Form.Label>
<Form.Select size="sm" isSuccess {...args}>
<Form.Select size="sm" {...args}>
<option>Small select</option>
<option value="1">One</option>
<option value="2">Two</option>
<option value="3">Three</option>
</Form.Select>
<FormText isSuccess>Success</FormText>
</Form.Group>
</FormGroup>
</>
)
},

View file

@ -1,49 +1,51 @@
import { Form } from 'react-bootstrap-5'
import type { Meta, StoryObj } from '@storybook/react'
import FormGroup from '@/features/ui/components/bootstrap-5/form/form-group'
import FormText from '@/features/ui/components/bootstrap-5/form/form-text'
import FormControl from '@/features/ui/components/bootstrap-5/form/form-control'
const meta: Meta<(typeof Form)['Control']> = {
const meta: Meta<React.ComponentProps<typeof FormControl>> = {
title: 'Shared / Components / Bootstrap 5 / Form / Textarea',
component: Form.Control,
component: FormControl,
parameters: {
bootstrap5: true,
},
}
export default meta
type Story = StoryObj<(typeof Form)['Control']>
type Story = StoryObj<React.ComponentProps<typeof FormControl>>
export const Default: Story = {
render: args => {
return (
<>
<Form.Group controlId="id-1">
<FormGroup controlId="id-1">
<Form.Label>Label</Form.Label>
<Form.Control
<FormControl
as="textarea"
defaultValue="Large input"
size="lg"
{...args}
/>
<FormText>Helper</FormText>
</Form.Group>
</FormGroup>
<hr />
<Form.Group controlId="id-2">
<FormGroup controlId="id-2">
<Form.Label>Label</Form.Label>
<Form.Control as="textarea" defaultValue="Regular input" {...args} />
<FormControl as="textarea" defaultValue="Regular input" {...args} />
<FormText>Helper</FormText>
</Form.Group>
</FormGroup>
<hr />
<Form.Group controlId="id-3">
<FormGroup controlId="id-3">
<Form.Label>Label</Form.Label>
<Form.Control
<FormControl
as="textarea"
defaultValue="Small input"
size="sm"
{...args}
/>
<FormText>Helper</FormText>
</Form.Group>
</FormGroup>
</>
)
},
@ -56,9 +58,9 @@ export const Info: Story = {
render: args => {
return (
<>
<Form.Group controlId="id-1">
<FormGroup controlId="id-1">
<Form.Label>Label</Form.Label>
<Form.Control
<FormControl
as="textarea"
placeholder="Placeholder"
defaultValue="Large input"
@ -66,22 +68,22 @@ export const Info: Story = {
{...args}
/>
<FormText isInfo>Info</FormText>
</Form.Group>
</FormGroup>
<hr />
<Form.Group controlId="id-2">
<FormGroup controlId="id-2">
<Form.Label>Label</Form.Label>
<Form.Control
<FormControl
as="textarea"
placeholder="Placeholder"
defaultValue="Regular input"
{...args}
/>
<FormText isInfo>Info</FormText>
</Form.Group>
</FormGroup>
<hr />
<Form.Group controlId="id-3">
<FormGroup controlId="id-3">
<Form.Label>Label</Form.Label>
<Form.Control
<FormControl
as="textarea"
placeholder="Placeholder"
defaultValue="Small input"
@ -89,7 +91,7 @@ export const Info: Story = {
{...args}
/>
<FormText isInfo>Info</FormText>
</Form.Group>
</FormGroup>
</>
)
},
@ -99,9 +101,9 @@ export const Error: Story = {
render: args => {
return (
<>
<Form.Group controlId="id-1">
<FormGroup controlId="id-1">
<Form.Label>Label</Form.Label>
<Form.Control
<FormControl
as="textarea"
placeholder="Placeholder"
defaultValue="Large input"
@ -110,11 +112,11 @@ export const Error: Story = {
{...args}
/>
<FormText isError>Error</FormText>
</Form.Group>
</FormGroup>
<hr />
<Form.Group controlId="id-2">
<FormGroup controlId="id-2">
<Form.Label>Label</Form.Label>
<Form.Control
<FormControl
as="textarea"
placeholder="Placeholder"
defaultValue="Regular input"
@ -122,11 +124,11 @@ export const Error: Story = {
{...args}
/>
<FormText isError>Error</FormText>
</Form.Group>
</FormGroup>
<hr />
<Form.Group controlId="id-3">
<FormGroup controlId="id-3">
<Form.Label>Label</Form.Label>
<Form.Control
<FormControl
as="textarea"
placeholder="Placeholder"
defaultValue="Small input"
@ -135,7 +137,7 @@ export const Error: Story = {
{...args}
/>
<FormText isError>Error</FormText>
</Form.Group>
</FormGroup>
</>
)
},
@ -145,9 +147,9 @@ export const Warning: Story = {
render: args => {
return (
<>
<Form.Group controlId="id-1">
<FormGroup controlId="id-1">
<Form.Label>Label</Form.Label>
<Form.Control
<FormControl
as="textarea"
placeholder="Placeholder"
defaultValue="Large input"
@ -155,22 +157,22 @@ export const Warning: Story = {
{...args}
/>
<FormText isWarning>Warning</FormText>
</Form.Group>
</FormGroup>
<hr />
<Form.Group controlId="id-2">
<FormGroup controlId="id-2">
<Form.Label>Label</Form.Label>
<Form.Control
<FormControl
as="textarea"
placeholder="Placeholder"
defaultValue="Regular input"
{...args}
/>
<FormText isWarning>Warning</FormText>
</Form.Group>
</FormGroup>
<hr />
<Form.Group controlId="id-3">
<FormGroup controlId="id-3">
<Form.Label>Label</Form.Label>
<Form.Control
<FormControl
as="textarea"
placeholder="Placeholder"
defaultValue="Small input"
@ -178,7 +180,7 @@ export const Warning: Story = {
{...args}
/>
<FormText isWarning>Warning</FormText>
</Form.Group>
</FormGroup>
</>
)
},
@ -188,9 +190,9 @@ export const Success: Story = {
render: args => {
return (
<>
<Form.Group controlId="id-1">
<FormGroup controlId="id-1">
<Form.Label>Label</Form.Label>
<Form.Control
<FormControl
as="textarea"
placeholder="Placeholder"
defaultValue="Large input"
@ -198,22 +200,22 @@ export const Success: Story = {
{...args}
/>
<FormText isSuccess>Success</FormText>
</Form.Group>
</FormGroup>
<hr />
<Form.Group controlId="id-2">
<FormGroup controlId="id-2">
<Form.Label>Label</Form.Label>
<Form.Control
<FormControl
as="textarea"
placeholder="Placeholder"
defaultValue="Regular input"
{...args}
/>
<FormText isSuccess>Success</FormText>
</Form.Group>
</FormGroup>
<hr />
<Form.Group controlId="id-3">
<FormGroup controlId="id-3">
<Form.Label>Label</Form.Label>
<Form.Control
<FormControl
as="textarea"
placeholder="Placeholder"
defaultValue="Small input"
@ -221,7 +223,7 @@ export const Success: Story = {
{...args}
/>
<FormText isSuccess>Success</FormText>
</Form.Group>
</FormGroup>
</>
)
},

View file

@ -386,10 +386,8 @@ ul.folders-menu {
form.project-search {
.form-group {
@media (min-width: @screen-md) {
margin-bottom: 0;
}
}
}
.project-search-clear-btn {

View file

@ -84,3 +84,69 @@
.form-group {
margin-bottom: var(--spacing-06);
}
.form-control-wrapper {
position: relative;
&.form-control-wrapper-disabled {
.form-control-start-icon,
.form-control-end-icon {
& > * {
color: var(--content-disabled);
}
}
}
.form-control-start-icon,
.form-control-end-icon {
position: absolute;
top: 0;
height: 100%;
display: flex;
align-items: center;
font-size: 0;
}
.form-control-start-icon {
left: 0;
}
.form-control-end-icon {
right: 0;
}
--icon-width: 20px;
--form-control-padding-x: var(--spacing-04);
--form-control-icon-offset-y: var(--spacing-04);
&.form-control-wrapper-sm {
--form-control-padding-x: var(--spacing-03);
--form-control-icon-offset-y: var(--spacing-02);
}
&.form-control-wrapper-lg {
--form-control-padding-x: var(--spacing-05);
--form-control-icon-offset-y: var(--spacing-05);
}
.form-control-start-icon {
padding-left: calc(var(--form-control-padding-x) + var(--bs-border-width));
}
.form-control-end-icon {
padding-right: calc(var(--form-control-padding-x) + var(--bs-border-width));
}
.form-control-offset-start {
padding-left: calc(
var(--form-control-padding-x) + var(--form-control-icon-offset-y) +
var(--icon-width)
);
}
.form-control-offset-end {
padding-right: calc(
var(--form-control-padding-x) + var(--form-control-icon-offset-y) +
var(--icon-width)
);
}
}

View file

@ -464,3 +464,13 @@
.project-list-load-more-button {
margin-bottom: var(--spacing-05);
}
form.project-search {
.form-group {
margin-bottom: 0;
}
}
.project-search-clear-btn {
@include reset-button;
}