Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
53167ab
wip
aresnik11 Aug 11, 2025
fff7888
Merge branch 'main' into ajr-nested-checkboxes
aresnik11 Sep 4, 2025
cef1cbf
working
aresnik11 Sep 5, 2025
3a53551
update types
aresnik11 Sep 8, 2025
b1d05a8
add aria-checked
aresnik11 Sep 9, 2025
05c00fb
update stories
aresnik11 Sep 9, 2025
ec5e503
fix connectednestedcheckbox
aresnik11 Sep 15, 2025
9562d55
dont use children as prop name
aresnik11 Sep 15, 2025
6dbaf28
Merge branch 'main' into ajr-nested-checkboxes
aresnik11 Sep 15, 2025
f8dbe30
add back in nested examples
aresnik11 Sep 15, 2025
309c8a3
fix errors
aresnik11 Sep 15, 2025
7d5ddfa
PR feedback
aresnik11 Sep 19, 2025
8f9a058
DRY up code
aresnik11 Sep 19, 2025
a34b0b5
first stab at tests
aresnik11 Sep 19, 2025
641aeb2
it works in gridform
aresnik11 Sep 23, 2025
86152dc
clean up logs and comments
aresnik11 Sep 23, 2025
5108c8c
fix defaultValue type issue
aresnik11 Sep 24, 2025
f67e05d
types refactor
aresnik11 Sep 24, 2025
fea9287
gridform default value
aresnik11 Sep 24, 2025
e2e3cc6
connectedform default value
aresnik11 Sep 24, 2025
8c65ca0
update connectedform
aresnik11 Sep 25, 2025
d6b147e
add passing tests
aresnik11 Sep 25, 2025
5694439
clean up tests
aresnik11 Sep 26, 2025
a562cc1
Merge branch 'main' into ajr-nested-checkboxes
aresnik11 Oct 8, 2025
82d2598
PR feedback
aresnik11 Oct 14, 2025
6208a48
Merge branch 'ajr-nested-checkboxes' of github.com:Codecademy/gamut i…
aresnik11 Oct 14, 2025
9a809ff
Merge branch 'main' into ajr-nested-checkboxes
aresnik11 Oct 14, 2025
b55a426
improvements
aresnik11 Oct 14, 2025
05f8fab
Merge branch 'ajr-nested-checkboxes' of github.com:Codecademy/gamut i…
aresnik11 Oct 15, 2025
18bc8fd
update gridform spacing
aresnik11 Oct 15, 2025
79cb2d9
revamp gridform docs
aresnik11 Oct 15, 2025
4a31dec
update spacing logic
aresnik11 Oct 16, 2025
61bc85d
Merge branch 'ajr-nested-checkboxes' into ajr-gridform-sb-docs
aresnik11 Oct 16, 2025
90cb56a
Merge branch 'main' into ajr-nested-checkboxes
aresnik11 Oct 16, 2025
b71d85f
Merge branch 'ajr-nested-checkboxes' into ajr-gridform-sb-docs
aresnik11 Oct 20, 2025
b920435
Merge branch 'main' into ajr-gridform-sb-docs
aresnik11 Oct 24, 2025
c380ed9
add deep controls to table of contents
aresnik11 Oct 24, 2025
253e249
remove unused
aresnik11 Oct 24, 2025
50ca32b
break down into pages
aresnik11 Oct 24, 2025
3d94191
clean up
aresnik11 Oct 24, 2025
33b6332
DRY up states
aresnik11 Oct 27, 2025
951a98f
fix it up
aresnik11 Oct 27, 2025
59ebec6
format
aresnik11 Oct 27, 2025
b491baa
Update packages/styleguide/src/lib/Organisms/GridForm/Fields.mdx
aresnik11 Oct 29, 2025
94029e1
Update packages/styleguide/src/lib/Organisms/GridForm/Fields.mdx
aresnik11 Oct 29, 2025
5aebe44
Update packages/styleguide/src/lib/Organisms/GridForm/Fields.mdx
aresnik11 Oct 29, 2025
0757139
Update packages/styleguide/src/lib/Organisms/GridForm/Layout.mdx
aresnik11 Oct 29, 2025
bddbdb3
Update packages/styleguide/src/lib/Organisms/GridForm/Fields.mdx
aresnik11 Oct 29, 2025
c22064f
Update packages/styleguide/src/lib/Organisms/GridForm/Validation.mdx
aresnik11 Oct 29, 2025
7c923dc
Update packages/styleguide/src/lib/Organisms/GridForm/Layout.mdx
aresnik11 Oct 29, 2025
4097e9a
Update packages/styleguide/src/lib/Organisms/GridForm/States.mdx
aresnik11 Oct 29, 2025
0506a55
Update packages/styleguide/src/lib/Organisms/GridForm/States.mdx
aresnik11 Oct 29, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/styleguide/src/lib/Meta/About.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
import { parameters as bestPracticesParameters } from './Best Practices.mdx';
import { parameters as brandParameters } from './Brand.mdx';
import { parameters as contributingParameters } from './Contributing.mdx';
import { parameters as deepControlsParameters } from './Deep Controls Add-On.mdx';
import { parameters as faqsParameters } from './FAQs.mdx';
import { parameters as installationParameters } from './Installation.mdx';
import { parameters as storiesParameters } from './Stories.mdx';
Expand All @@ -29,6 +30,7 @@ export const parameters = {
links={addParentPath(parameters.id, [
bestPracticesParameters,
contributingParameters,
deepControlsParameters,
faqsParameters,
storiesParameters,
brandParameters,
Expand Down
7 changes: 7 additions & 0 deletions packages/styleguide/src/lib/Meta/Deep Controls Add-On.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@ import { Meta } from '@storybook/blocks';

import { Callout, ImageWrapper, LinkTo } from '~styleguide/blocks';

export const parameters = {
id: 'Deep Controls Add-On',
title: 'Deep Controls Add-On',
subtitle: `Enables Storybook controls for nested component properties, allowing you to interactively modify deeply nested props directly from the Controls panel without having to manually edit complex object structures.`,
status: 'static',
};

<Meta title="Meta/Deep Controls add-on" />

# Deep Controls add-on
Expand Down
2 changes: 1 addition & 1 deletion packages/styleguide/src/lib/Organisms/About.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
} from '~styleguide/blocks';

import { parameters as connectedFormParameters } from './ConnectedForm/About.mdx';
import { parameters as gridFormParameters } from './GridForm/GridForm.mdx';
import { parameters as gridFormParameters } from './GridForm/About.mdx';
import { parameters as listsTablesParameters } from './Lists & Tables/About.mdx';
import { parameters as markdownParameters } from './Markdown/Markdown.mdx';

Expand Down
35 changes: 35 additions & 0 deletions packages/styleguide/src/lib/Organisms/GridForm/About.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Meta } from '@storybook/blocks';

import {
AboutHeader,
addParentPath,
TableOfContents,
} from '~styleguide/blocks';

import { parameters as buttonsParameters } from './Buttons.mdx';
import { parameters as fieldsParameters } from './Fields.mdx';
import { parameters as layoutParameters } from './Layout.mdx';
import { parameters as statesParameters } from './States.mdx';
import { parameters as usageParameters } from './Usage.mdx';
import { parameters as validationParameters } from './Validation.mdx';

export const parameters = {
id: 'Organisms/GridForm',
title: 'GridForm',
subtitle: 'An efficient way to build and design forms on a grid.',
};

<Meta title="Organisms/GridForm/About" />

<AboutHeader {...parameters} />

Copy link
Contributor

@LinKCoding LinKCoding Oct 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Food for thought: It'd be nice if there was some more info on how to navigate these files, esp for first time users.

E.g.
If you need an overview of how GridForm works and to interact with a playground, go to <LinkTo id="..../Usage">Usage</LinkTo>
All other pages provide guidance on specific parts of GridForm.

<TableOfContents
links={addParentPath(parameters.id, [
usageParameters,
fieldsParameters,
buttonsParameters,
validationParameters,
layoutParameters,
statesParameters,
])}
/>
68 changes: 68 additions & 0 deletions packages/styleguide/src/lib/Organisms/GridForm/Buttons.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { Canvas, Meta } from '@storybook/blocks';

import { ComponentHeader, LinkTo } from '~styleguide/blocks';

import * as ButtonsStories from './Buttons.stories';

export const parameters = {
title: 'Buttons',
subtitle: 'Configure submit and cancel buttons for your forms.',
};

<Meta title="Organisms/GridForm/Buttons" />

<ComponentHeader {...parameters} />

## Submit button position

We can position the submit button by passing the `position` prop within `submit` with a
value of `'left'`, `'center'`, `'right'`, or `'stretch'`. The default is `'left'`.

<Canvas of={ButtonsStories.SubmitButtonRight} />

<Canvas of={ButtonsStories.SubmitButtonLeft} />

<Canvas of={ButtonsStories.SubmitButtonCenter} />

<Canvas of={ButtonsStories.SubmitButtonStretch} />

## Submit button type

We can specify the type submit button by passing the `type` prop within `submit`. We can choose between
the <LinkTo id="atoms-button--fill-button">`FillButton`</LinkTo> or <LinkTo id="atoms-button--cta-button">`CTAButton`</LinkTo>. The default is `'fill'`.

<Canvas of={ButtonsStories.SubmitButtonFill} />

<Canvas of={ButtonsStories.SubmitButtonCTA} />

## Inline submit button

We can make the Submit button inline with an input by setting the column
sizes so they fit on the same row (e.g size 8 for an input and size 4 for
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the actual example it has input of size 6 and the submit button's size 4, should the example follow the guidance in this MDX file?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch, i updated them to all use the shared args but will make this match

the submit).

We can additionally remove the label from text inputs and checkbox inputs.
Use the `hideLabel` prop to remove the label, allowing the submit button to
align nicely with the input. **However**, if using `hideLabel` to remove the default label, you should provide an `aria-label` and/or include another label to the right/left of the input to ensure the input is accessible.

<Canvas of={ButtonsStories.SubmitButtonInline} />

## Cancel button

Optionally, include a cancel button.

<Canvas of={ButtonsStories.CancelButton} />

## Button states

### Loading

We can set the state of the submit button to `loading` as `true` to show a loading spinner. This is useful when you need to show the user that the form is submitting.

<Canvas of={ButtonsStories.Loading} />

### Disabled

You can also set `disabled` to `true` to disable submission.

<Canvas of={ButtonsStories.Disabled} />
137 changes: 137 additions & 0 deletions packages/styleguide/src/lib/Organisms/GridForm/Buttons.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import { GridForm } from '@codecademy/gamut';
import { action } from '@storybook/addon-actions';
import type { Meta, StoryObj } from '@storybook/react';

const meta: Meta<typeof GridForm> = {
component: GridForm,
args: {
onSubmit: (values) => {
action('Form Submitted')(values);
// eslint-disable-next-line no-console
console.log('Form Submitted', values);
},
fields: [
{
label: 'Simple text',
name: 'simple-text',
type: 'text',
size: 12,
},
],
},
};

export default meta;
type Story = StoryObj<typeof GridForm>;

export const SubmitButtonRight: Story = {
args: {
submit: {
contents: 'Right Submit!?',
position: 'right',
size: 12,
},
},
};

export const SubmitButtonLeft: Story = {
args: {
submit: {
contents: 'Left Submit!?',
position: 'left',
size: 12,
},
},
};

export const SubmitButtonCenter: Story = {
args: {
submit: {
contents: 'Center Submit!?',
position: 'center',
size: 12,
},
},
};

export const SubmitButtonStretch: Story = {
args: {
submit: {
contents: 'Stretch Submit!?',
position: 'stretch',
size: 12,
},
},
};

export const SubmitButtonFill: Story = {
args: {
submit: {
contents: 'Fill Submit!?',
type: 'fill',
size: 12,
},
},
};

export const SubmitButtonCTA: Story = {
args: {
submit: {
contents: 'CTA Submit!?',
type: 'cta',
size: 12,
},
},
};

export const SubmitButtonInline: Story = {
args: {
fields: [
{
label: 'Simple text',
name: 'simple-text',
type: 'text',
size: 6,
},
],
submit: {
contents: 'Inline Submit!?',
size: 4,
position: 'right',
},
},
};

export const CancelButton: Story = {
args: {
cancel: {
children: 'Cancel',
onClick: () => {},
},
submit: {
contents: 'Submit!?',
position: 'right',
size: 12,
},
},
};

export const Loading: Story = {
args: {
submit: {
contents: 'Loading Submit!?',
loading: true,
size: 12,
},
},
};

export const Disabled: Story = {
args: {
submit: {
contents: 'Disabled Submit!?',
disabled: true,
size: 12,
},
},
};
96 changes: 96 additions & 0 deletions packages/styleguide/src/lib/Organisms/GridForm/Fields.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import { Canvas, Meta } from '@storybook/blocks';

import { ComponentHeader } from '~styleguide/blocks';

import * as FieldsStories from './Fields.stories';

export const parameters = {
title: 'Fields',
subtitle: 'Comprehensive GridForm field types to cover various input needs.',
};

<Meta title="Organisms/GridForm/Fields" />

<ComponentHeader {...parameters} />

## Text inputs

Text inputs support various HTML input types including `text`, `email`, `password`, `number`, `tel`, `url`, `search`, `date`, `time`, and more. All text inputs share the same basic properties but may have different validation patterns.

<Canvas of={FieldsStories.TextField} />

### Default value

<Canvas of={FieldsStories.DefaultTextField} />

### Placeholder text

Text inputs are allowed to have traditional `placeholder` text.
This is a somewhat dangerous behavior for accessibility, as browsers
generally don't render placeholder text with high enough color contrast
for AA standards. If you do need to use placeholder text, such as on
landing page forms that have been shown to have higher completion rates
with the text, please make sure the placeholder text doesn't add any new
information to the form — it should really only rephrase the text label.

See [this article](https://www.nngroup.com/articles/form-design-placeholders/) for
more details on why using placeholders is often bad.

<Canvas of={FieldsStories.PlaceholderTextField} />

## Textarea input

<Canvas of={FieldsStories.TextareaField} />

## Select input

<Canvas of={FieldsStories.SelectField} />

## Radio group input

<Canvas of={FieldsStories.RadioGroupField} />

## File upload input

File upload fields allow users to select and upload files. You can add custom validation to restrict file types and sizes.

<Canvas of={FieldsStories.FileUploadField} />

## Checkbox input

<Canvas of={FieldsStories.CheckboxField} />

### Spacing

Checkboxes can use tight spacing when you need them to fit in smaller areas:

<Canvas of={FieldsStories.CheckboxSpacing} />
Comment on lines +61 to +67
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These examples' are a little confusing b.c. the overall form has *Required

but the actual checkboxes fields are optional

Image


## Nested checkboxes input

Nested checkboxes allow for hierarchical selection with parent-child relationships between options. Infinite levels of nesting are supported. Clicking a parent checkbox toggles all its children accordingly. Individual child checkboxes can be toggled independently. Checkbox states are `checked` if all children are checked, `indeterminate` if some children are checked, or `unchecked` if no children are checked. The values returned by the form on submit or update are an array of all selected values, including all children.

<Canvas of={FieldsStories.NestedCheckboxesField} />

## Custom inputs

Some forms, such as the checkout flows that use Recurly, need to define
their own inputs. We can specify a `'custom'` field type along with a [`render` prop](https://reactjs.org/docs/render-props.html).

We also have a `'custom-group'` type for when you are passing in a custom `FormGroup` - including a label. If you do not want `GridForm` to surface errors for your field, you should likely use a `'custom-group'`. If you chose to go this route, please be aware of [accessibility best practices](https://www.deque.com/blog/anatomy-of-accessible-forms-best-practices/) for forms.

<Canvas of={FieldsStories.CustomInputs} />

## Hidden input

Hidden inputs can be used to include data that users can't see or modify with the submission. For this implementation you can set the `defaultValue` in the object and it will be submitted with the regular form data.

<Canvas of={FieldsStories.HiddenInput} />

## Sweet container input

"Sweet container" ([honeypot](<https://en.wikipedia.org/wiki/Honeypot_(computing)>)) inputs can be used to detect bots by providing a field that would not generally be clicked by human users, but might be triggered automatically by bots.

We call it a "sweet container" so that bots do not immediately detect it as a honeypot input.

<Canvas of={FieldsStories.SweetContainer} />
Loading
Loading