Skip to content

Commit

Permalink
Merge pull request #107 from fortanix/feature/textarea
Browse files Browse the repository at this point in the history
New component: TextArea
  • Loading branch information
mkrause authored Jan 16, 2025
2 parents e9ed54b + 6ea8125 commit 000b384
Show file tree
Hide file tree
Showing 8 changed files with 375 additions and 1 deletion.
2 changes: 2 additions & 0 deletions app/lib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export { Radio } from '../src/components/forms/controls/Radio/Radio.tsx';
export { SegmentedControl } from '../src/components/forms/controls/SegmentedControl/SegmentedControl.tsx';
export { Select } from '../src/components/forms/controls/Select/Select.tsx';
export { Switch } from '../src/components/forms/controls/Switch/Switch.tsx';
export { TextArea } from '../src/components/forms/controls/TextArea/TextArea.tsx';
export { TimePicker } from '../src/components/forms/controls/TimePicker/TimePicker.tsx';

// Forms > Fields
Expand All @@ -47,6 +48,7 @@ export { InputField } from '../src/components/forms/fields/InputField/InputField
export { InputFieldWithTags } from '../src/components/forms/fields/InputFieldWithTags/InputFieldWithTags.tsx';
export { RadioField } from '../src/components/forms/fields/RadioField/RadioField.tsx';
export { RadioGroup } from '../src/components/forms/fields/RadioGroup/RadioGroup.tsx';
export { TextAreaField } from '../src/components/forms/fields/TextAreaField/TextAreaField.tsx';

// Graphics
export { Icon } from '../src/components/graphics/Icon/Icon.tsx';
Expand Down
51 changes: 51 additions & 0 deletions src/components/forms/controls/TextArea/TextArea.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/* 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-text-area {
@include bk.component-base(bk-textarea);

@include bk.focus-hidden;

--bk-text-area-border-color: #{bk.$theme-text-area-border-filled};

border: bk.rem-from-px(1) solid var(--bk-text-area-border-color);
border-radius: bk.$radius-1;
color: bk.$theme-text-area-text-filled;
padding: bk.$spacing-3;

&::placeholder {
color: bk.$theme-text-area-text-default;
}

&:placeholder-shown {
--bk-text-area-border-color: #{bk.$theme-text-area-border-default};
}

&:disabled {
color: bk.$theme-text-area-text-disabled;
--bk-text-area-border-color: #{bk.$theme-text-area-border-disabled};
}
}

.bk-text-area--invalid, .bk-text-area--invalid:placeholder-shown {
--bk-text-area-border-color: #{bk.$theme-text-area-border-error};
}

.bk-text-area:focus,
.bk-text-area.pseudo-focused,
.bk-text-area--invalid:focus,
.bk-text-area--invalid.pseudo-focused,
.bk-text-area--invalid:placeholder-shown:focus,
.bk-text-area--invalid:placeholder-shown.pseudo-focused {
color: bk.$theme-text-area-text-focused;
--bk-text-area-border-color: #{bk.$theme-text-area-border-focused};
}

.bk-text-area--automatic-resize {
field-sizing: content;
}
}
93 changes: 93 additions & 0 deletions src/components/forms/controls/TextArea/TextArea.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/* 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 { TextArea } from './TextArea.tsx';

import cl from './TextArea.module.scss';

type TextAreaArgs = React.ComponentProps<typeof TextArea>;
type Story = StoryObj<TextAreaArgs>;

export default {
component: TextArea,
parameters: {
layout: 'centered',
},
tags: ['autodocs'],
argTypes: {
},
args: {
placeholder: 'Example',
},
decorators: [
Story => <form onSubmit={event => { event.preventDefault(); }}><Story/></form>,
],
render: (args) => <TextArea {...args}/>,
} satisfies Meta<TextAreaArgs>;

const longText = `A really
long
text
that
spans
several
lines`;

export const Placeholder: Story = {};

export const Filled: Story = {
args: {
defaultValue: 'Some text',
},
};

export const Focused: Story = {
args: {
className: cl['pseudo-focused'],
}
};

export const Disabled: Story = {
args: {
disabled: true,
},
};

export const Invalid: Story = {
args: {
invalid: true,
},
};

export const ScrollBar: Story = {
args: {
defaultValue: longText,
},
};

export const AutomaticResize: Story = {
args: {
automaticResize: true,
},
};

export const AutomaticVerticalResize: Story = {
args: {
automaticResize: true,
defaultValue: longText,
style: { width: 300 },
},
};

export const AutomaticHorizontalResize: Story = {
args: {
automaticResize: true,
defaultValue: longText,
style: { height: 100 },
},
};
47 changes: 47 additions & 0 deletions src/components/forms/controls/TextArea/TextArea.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/* 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 cl from './TextArea.module.scss';


export { cl as TextAreaClassNames };

export type TextAreaProps = Omit<ComponentProps<'textarea'>, 'type'> & {
/** Whether this component should be unstyled. */
unstyled?: undefined | boolean,

/**
* Whether the textarea should resize automatically, with `field-sizing: content`.
* Note that browser support is still somewhat limited:
* https://developer.mozilla.org/en-US/docs/Web/CSS/field-sizing
*/
automaticResize?: undefined | boolean,

/** Whether the component should be styled as invalid, i.e. having invalid content or failed validation. */
invalid?: undefined | boolean,
};

/**
* TextArea control.
*/
export const TextArea = ({
unstyled = false,
automaticResize = false,
invalid = false,
...propsRest
}: TextAreaProps) => {
return (
<textarea
{...propsRest}
className={cx({
bk: true,
[cl['bk-text-area']]: !unstyled,
[cl['bk-text-area--automatic-resize']]: automaticResize,
[cl['bk-text-area--invalid']]: invalid,
}, propsRest.className)}
/>
);
};
1 change: 0 additions & 1 deletion src/components/forms/fields/InputField/InputField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import * as React from 'react';

import { useFormContext } from '../../context/Form/Form.tsx';
import { Input } from '../../controls/Input/Input.tsx';
import { Tag } from '../../../text/Tag/Tag.tsx';

import cl from './InputField.module.scss';

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/* 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-text-area-field {
@include bk.component-base(bk-text-area-field);

display: flex;
flex-direction: column;
gap: bk.rem-from-px(4);

.bk-text-area-field__label {
@include bk.font(bk.$font-family-body, bk.$font-weight-semibold);
cursor: default;
}

.bk-text-area-field__label__optional {
@include bk.font(bk.$font-family-body, bk.$font-weight-regular, bk.$font-size-xs);
margin-left: bk.rem-from-px(4);
}

.bk-text-area-field__control {
--empty: ; // Prevent empty class from being removed
}

.bk-text-area-field__hint {
@include bk.font(bk.$font-family-body, bk.$font-weight-regular, bk.$font-size-xs);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/* 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 { Form } from '../../context/Form/Form.tsx';

import { TextAreaField } from './TextAreaField.tsx';


type TextAreaArgs = React.ComponentProps<typeof TextAreaField>;
type Story = StoryObj<TextAreaArgs>;

export default {
component: TextAreaField,
parameters: {
layout: 'centered',
},
tags: ['autodocs'],
argTypes: {
},
args: {
label: 'Label',
placeholder: 'Example',
},
decorators: [
Story => <Form><Story/></Form>,
],
render: (args) => <TextAreaField {...args}/>,
} satisfies Meta<TextAreaArgs>;

export const Standard: Story = {};

export const Optional: Story = {
args: {
optional: true,
},
};

export const WithHint: Story = {
args: {
hint: 'Hint/error',
},
};

export const OptionalWithHint: Story = {
args: {
optional: true,
hint: 'Hint/error',
},
};

export const ScrollBar: Story = {
args: {
defaultValue: `A really
long
text
that
spans
several
lines`,
hint: 'Hint/error',
},
};
Loading

0 comments on commit 000b384

Please sign in to comment.