diff --git a/app/javascript/flavours/glitch/components/form_fields/form_stack.module.scss b/app/javascript/flavours/glitch/components/form_fields/form_stack.module.scss
new file mode 100644
index 0000000000..083e36c320
--- /dev/null
+++ b/app/javascript/flavours/glitch/components/form_fields/form_stack.module.scss
@@ -0,0 +1,7 @@
+.stack {
+ box-sizing: border-box;
+ display: flex;
+ flex-direction: column;
+ gap: 25px;
+ padding: 16px;
+}
diff --git a/app/javascript/flavours/glitch/components/form_fields/form_stack.tsx b/app/javascript/flavours/glitch/components/form_fields/form_stack.tsx
new file mode 100644
index 0000000000..707545898e
--- /dev/null
+++ b/app/javascript/flavours/glitch/components/form_fields/form_stack.tsx
@@ -0,0 +1,23 @@
+import classNames from 'classnames';
+
+import { polymorphicForwardRef } from '@/types/polymorphic';
+
+import classes from './form_stack.module.scss';
+
+/**
+ * A simple wrapper for providing consistent spacing to a group of form fields.
+ */
+
+export const FormStack = polymorphicForwardRef<'div'>(
+ ({ as: Element = 'div', children, className, ...otherProps }, ref) => (
+
+ {children}
+
+ ),
+);
+
+FormStack.displayName = 'FormStack';
diff --git a/app/javascript/flavours/glitch/components/form_fields/index.ts b/app/javascript/flavours/glitch/components/form_fields/index.ts
index 76137dd37a..f87626cb65 100644
--- a/app/javascript/flavours/glitch/components/form_fields/index.ts
+++ b/app/javascript/flavours/glitch/components/form_fields/index.ts
@@ -1,5 +1,6 @@
-export { TextInputField } from './text_input_field';
-export { TextAreaField } from './text_area_field';
+export { FormStack } from './form_stack';
+export { TextInputField, TextInput } from './text_input_field';
+export { TextAreaField, TextArea } from './text_area_field';
export { CheckboxField, Checkbox } from './checkbox_field';
export { ToggleField, Toggle } from './toggle_field';
export { SelectField, Select } from './select_field';
diff --git a/app/javascript/flavours/glitch/components/form_fields/text_area_field.stories.tsx b/app/javascript/flavours/glitch/components/form_fields/text_area_field.stories.tsx
index f4b8440916..448af8a28e 100644
--- a/app/javascript/flavours/glitch/components/form_fields/text_area_field.stories.tsx
+++ b/app/javascript/flavours/glitch/components/form_fields/text_area_field.stories.tsx
@@ -1,6 +1,6 @@
import type { Meta, StoryObj } from '@storybook/react-vite';
-import { TextAreaField } from './text_area_field';
+import { TextAreaField, TextArea } from './text_area_field';
const meta = {
title: 'Components/Form Fields/TextAreaField',
@@ -9,14 +9,6 @@ const meta = {
label: 'Label',
hint: 'This is a description of this form field',
},
- render(args) {
- // Component styles require a wrapper class at the moment
- return (
-
-
-
- );
- },
} satisfies Meta;
export default meta;
@@ -49,3 +41,17 @@ export const WithError: Story = {
hasError: true,
},
};
+
+export const Plain: Story = {
+ render(args) {
+ return ;
+ },
+};
+
+export const Disabled: Story = {
+ ...Plain,
+ args: {
+ disabled: true,
+ defaultValue: "This value can't be changed",
+ },
+};
diff --git a/app/javascript/flavours/glitch/components/form_fields/text_area_field.tsx b/app/javascript/flavours/glitch/components/form_fields/text_area_field.tsx
index fd514a88e2..bbde89574f 100644
--- a/app/javascript/flavours/glitch/components/form_fields/text_area_field.tsx
+++ b/app/javascript/flavours/glitch/components/form_fields/text_area_field.tsx
@@ -1,8 +1,11 @@
import type { ComponentPropsWithoutRef } from 'react';
import { forwardRef } from 'react';
+import classNames from 'classnames';
+
import { FormFieldWrapper } from './form_field_wrapper';
import type { CommonFieldWrapperProps } from './form_field_wrapper';
+import classes from './text_input.module.scss';
interface Props
extends ComponentPropsWithoutRef<'textarea'>, CommonFieldWrapperProps {}
@@ -23,9 +26,22 @@ export const TextAreaField = forwardRef(
hasError={hasError}
inputId={id}
>
- {(inputProps) => }
+ {(inputProps) => }
),
);
TextAreaField.displayName = 'TextAreaField';
+
+export const TextArea = forwardRef<
+ HTMLTextAreaElement,
+ ComponentPropsWithoutRef<'textarea'>
+>(({ className, ...otherProps }, ref) => (
+
+));
+
+TextArea.displayName = 'TextArea';
diff --git a/app/javascript/flavours/glitch/components/form_fields/text_input.module.scss b/app/javascript/flavours/glitch/components/form_fields/text_input.module.scss
new file mode 100644
index 0000000000..2299068c5a
--- /dev/null
+++ b/app/javascript/flavours/glitch/components/form_fields/text_input.module.scss
@@ -0,0 +1,42 @@
+.input {
+ box-sizing: border-box;
+ display: block;
+ resize: vertical;
+ width: 100%;
+ padding: 10px 16px;
+ font-family: inherit;
+ font-size: 14px;
+ line-height: 20px;
+ color: var(--color-text-primary);
+ background: var(--color-bg-secondary);
+ border: 1px solid var(--color-border-primary);
+ border-radius: 4px;
+ outline: var(--outline-focus-default);
+ outline-color: transparent;
+ outline-offset: -1px;
+ transition: outline-color 0.15s ease-out;
+
+ @media screen and (width <= 600px) {
+ font-size: 16px;
+ }
+
+ &:focus {
+ outline-color: var(--color-text-brand);
+ }
+
+ &:focus:user-invalid,
+ &:required:user-invalid,
+ [data-has-error='true'] & {
+ outline-color: var(--color-text-error);
+ }
+
+ &:required:user-valid {
+ outline-color: var(--color-text-success);
+ }
+
+ &:disabled {
+ color: var(--color-text-disabled);
+ border-color: transparent;
+ cursor: not-allowed;
+ }
+}
diff --git a/app/javascript/flavours/glitch/components/form_fields/text_input_field.stories.tsx b/app/javascript/flavours/glitch/components/form_fields/text_input_field.stories.tsx
index ec00ef5fd3..2cf8613f68 100644
--- a/app/javascript/flavours/glitch/components/form_fields/text_input_field.stories.tsx
+++ b/app/javascript/flavours/glitch/components/form_fields/text_input_field.stories.tsx
@@ -1,6 +1,6 @@
import type { Meta, StoryObj } from '@storybook/react-vite';
-import { TextInputField } from './text_input_field';
+import { TextInputField, TextInput } from './text_input_field';
const meta = {
title: 'Components/Form Fields/TextInputField',
@@ -9,14 +9,6 @@ const meta = {
label: 'Label',
hint: 'This is a description of this form field',
},
- render(args) {
- // Component styles require a wrapper class at the moment
- return (
-
-
-
- );
- },
} satisfies Meta;
export default meta;
@@ -49,3 +41,17 @@ export const WithError: Story = {
hasError: true,
},
};
+
+export const Plain: Story = {
+ render(args) {
+ return ;
+ },
+};
+
+export const Disabled: Story = {
+ ...Plain,
+ args: {
+ disabled: true,
+ defaultValue: "This value can't be changed",
+ },
+};
diff --git a/app/javascript/flavours/glitch/components/form_fields/text_input_field.tsx b/app/javascript/flavours/glitch/components/form_fields/text_input_field.tsx
index 3b2d941173..37cf150147 100644
--- a/app/javascript/flavours/glitch/components/form_fields/text_input_field.tsx
+++ b/app/javascript/flavours/glitch/components/form_fields/text_input_field.tsx
@@ -1,8 +1,11 @@
import type { ComponentPropsWithoutRef } from 'react';
import { forwardRef } from 'react';
+import classNames from 'classnames';
+
import { FormFieldWrapper } from './form_field_wrapper';
import type { CommonFieldWrapperProps } from './form_field_wrapper';
+import classes from './text_input.module.scss';
interface Props
extends ComponentPropsWithoutRef<'input'>, CommonFieldWrapperProps {}
@@ -15,10 +18,7 @@ interface Props
*/
export const TextInputField = forwardRef(
- (
- { id, label, hint, hasError, required, type = 'text', ...otherProps },
- ref,
- ) => (
+ ({ id, label, hint, hasError, required, ...otherProps }, ref) => (
(
hasError={hasError}
inputId={id}
>
- {(inputProps) => (
-
- )}
+ {(inputProps) => }
),
);
TextInputField.displayName = 'TextInputField';
+
+export const TextInput = forwardRef<
+ HTMLInputElement,
+ ComponentPropsWithoutRef<'input'>
+>(({ type = 'text', className, ...otherProps }, ref) => (
+
+));
+
+TextInput.displayName = 'TextInput';
diff --git a/app/javascript/flavours/glitch/features/collections/editor.tsx b/app/javascript/flavours/glitch/features/collections/editor.tsx
index 7cf704b265..11e364f773 100644
--- a/app/javascript/flavours/glitch/features/collections/editor.tsx
+++ b/app/javascript/flavours/glitch/features/collections/editor.tsx
@@ -18,6 +18,7 @@ import { Column } from 'flavours/glitch/components/column';
import { ColumnHeader } from 'flavours/glitch/components/column_header';
import {
CheckboxField,
+ FormStack,
TextAreaField,
} from 'flavours/glitch/components/form_fields';
import { TextInputField } from 'flavours/glitch/components/form_fields/text_input_field';
@@ -132,88 +133,80 @@ const CollectionSettings: React.FC<{
);
return (
-
+
);
};