Skip to content

feat: add Loading component and loading states across components - #254

Open
seanrcollings wants to merge 9 commits into
mainfrom
sc/loading-component
Open

seanrcollings wants to merge 9 commits into
mainfrom
sc/loading-component

Conversation

@seanrcollings

@seanrcollings seanrcollings commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Adds a new <Loading> ambient provider component and useLoading() hook, so a group of fields can be marked as loading from one place instead of threading isLoading through every child.
  • Adds isLoading/loadingLabel support (skeleton loading states) to Input, TextArea, ComboInput, TextField, NumberField/NumberInput, SearchField/SearchInput, TextInput, TextAreaInput, CheckBox, ToggleSwitch, RadioGroup, FileInput, CustomSelect, MultiSelect, ComboBox, Table, and wires Button/IconButton/LoadingStatus up to the same ambient useLoading() context.
  • Adds Storybook stories demonstrating the loading state for each of the above components, plus a new Loading stories file with simple examples and a more complex example form.
  • Adds a "Loading States" tab to the playground consolidating all of the above (merging in the old standalone Loading tab).

Screenshots

image image

Test plan

  • npm run build --workspace @atomicjolt/atomic-elements passes
  • Targeted typecheck of all new/edited Storybook stories passes
  • Manual pass through the new "Loading States" playground tab and Storybook Feedback/Loading stories in a browser

Copilot AI lite review requested due to automatic review settings September 17, 2026 16:25

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Changes recommended

Four unresolved moderate issues affect loading labels, accessibility, and FileInput loading behavior.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Adds an ambient Loading provider and hook, skeleton loading states across components, and updated Storybook/playground examples.

Changes:

  • Adds loading context, translations, and component integrations.
  • Updates fields, inputs, dropdowns, tables, buttons, and feedback components.
  • Adds stories, snapshots, playground content, and changeset metadata.
File summaries
File Summary
playground/src/tabs/LoadingStates.tsx Adds loading demonstrations.
playground/src/tabs/Loading.tsx Removes superseded loading tab.
playground/src/tabs/Fields.tsx Adds ambient loading example.
playground/src/Playground.tsx Registers Loading States tab.
packages/atomic-elements/src/types/fields.ts Adds loading field props.
packages/atomic-elements/src/types/common.ts Updates loading definitions.
packages/atomic-elements/src/i18n/en-US.ts Adds loading translation.
packages/atomic-elements/src/components/Internal/BaseButton.tsx Integrates ambient loading.
packages/atomic-elements/src/components/Inputs/ToggleSwitch/ToggleSwitch.styles.ts Adds loading styles.
packages/atomic-elements/src/components/Inputs/ToggleSwitch/ToggleSwitch.stories.tsx Adds loading story.
packages/atomic-elements/src/components/Inputs/ToggleSwitch/ToggleSwitch.component.tsx Adds toggle loading state.
packages/atomic-elements/src/components/Inputs/TextInput/TextInput.stories.tsx Adds loading story.
packages/atomic-elements/src/components/Inputs/TextInput/__snapshots__/TextInput.spec.tsx.snap Updates snapshots.
packages/atomic-elements/src/components/Inputs/TextAreaInput/TextAreaInput.stories.tsx Adds loading story.
packages/atomic-elements/src/components/Inputs/TextAreaInput/__snapshots__/TextAreaInput.spec.tsx.snap Updates snapshots.
packages/atomic-elements/src/components/Inputs/SearchInput/SearchInput.stories.tsx Adds loading story.
packages/atomic-elements/src/components/Inputs/SearchInput/__snapshots__/SearchInput.spec.tsx.snap Updates snapshots.
packages/atomic-elements/src/components/Inputs/RadioGroup/RadioGroup.styles.ts Adds loading styles.
packages/atomic-elements/src/components/Inputs/RadioGroup/RadioGroup.stories.tsx Adds loading story.
packages/atomic-elements/src/components/Inputs/RadioGroup/RadioGroup.component.tsx Adds radio loading state.
packages/atomic-elements/src/components/Inputs/RadioGroup/__snapshots__/RadioGroup.spec.tsx.snap Updates snapshots.
packages/atomic-elements/src/components/Inputs/NumberInput/NumberInput.stories.tsx Adds loading story.
packages/atomic-elements/src/components/Inputs/FileInput/FileInput.styles.ts Adds loading styles.
packages/atomic-elements/src/components/Inputs/FileInput/FileInput.stories.tsx Adds loading story.
packages/atomic-elements/src/components/Inputs/FileInput/FileInput.component.tsx Adds file input loading state.
packages/atomic-elements/src/components/Inputs/DateAndTimes/DateInput/__snapshots__/DateInput.spec.tsx.snap Updates snapshots.
packages/atomic-elements/src/components/Inputs/Checkbox/Checkbox.styles.ts Adds loading styles.
packages/atomic-elements/src/components/Inputs/Checkbox/Checkbox.stories.tsx Adds loading story.
packages/atomic-elements/src/components/Inputs/Checkbox/Checkbox.component.tsx Adds checkbox loading state.
packages/atomic-elements/src/components/Inputs/Checkbox/__snapshots__/CheckBox.spec.tsx.snap Updates snapshots.
packages/atomic-elements/src/components/index.ts Exports loading APIs.
packages/atomic-elements/src/components/Fields/TextField/TextField.stories.tsx Adds loading story.
packages/atomic-elements/src/components/Fields/TextField/TextField.component.tsx Adds text field loading support.
packages/atomic-elements/src/components/Fields/SelectField/__snapshots__/SelectField.spec.tsx.snap Updates snapshots.
packages/atomic-elements/src/components/Fields/SearchField/SearchField.component.tsx Propagates loading context.
packages/atomic-elements/src/components/Fields/NumberField/NumberField.component.tsx Propagates loading context.
packages/atomic-elements/src/components/Fields/MultiSelectField/__snapshots__/MultiSelectField.spec.tsx.snap Updates snapshots.
packages/atomic-elements/src/components/Fields/DateField/__snapshots__/DateField.spec.tsx.snap Updates snapshots.
packages/atomic-elements/src/components/Fields/ComboInput/ComboInput.types.ts Adds loading types.
packages/atomic-elements/src/components/Fields/ComboInput/ComboInput.styles.ts Adds loading styles.
packages/atomic-elements/src/components/Fields/ComboInput/ComboInput.stories.tsx Adds loading story.
packages/atomic-elements/src/components/Fields/ComboInput/ComboInput.component.tsx Adds combo input loading state.
packages/atomic-elements/src/components/Fields/ComboBoxField/ComboBoxField.types.ts Adds loading types.
packages/atomic-elements/src/components/Fields/ComboBoxField/ComboBoxField.component.tsx Propagates loading context.
packages/atomic-elements/src/components/Fields/ChipGroupField/__snapshots__/ChipGroupField.spec.tsx.snap Updates snapshots.
packages/atomic-elements/src/components/Fields/Atoms/TextArea/TextArea.styles.ts Adds loading styles.
packages/atomic-elements/src/components/Fields/Atoms/TextArea/TextArea.stories.tsx Adds loading story.
packages/atomic-elements/src/components/Fields/Atoms/TextArea/TextArea.component.tsx Adds textarea loading state.
packages/atomic-elements/src/components/Fields/Atoms/Message/Message.component.tsx Adjusts DOM prop forwarding.
packages/atomic-elements/src/components/Fields/Atoms/Label/Label.component.tsx Adjusts label prop forwarding.
packages/atomic-elements/src/components/Fields/Atoms/Input/Input.stories.tsx Adds loading story.
packages/atomic-elements/src/components/Fields/Atoms/Input/Input.component.tsx Adds input loading state.
packages/atomic-elements/src/components/Fields/Atoms/ErrorMessage/ErrorMessage.component.tsx Adjusts DOM prop forwarding.
packages/atomic-elements/src/components/Feedback/LoadingStatus/LoadingStatus.component.tsx Integrates ambient loading.
packages/atomic-elements/src/components/Feedback/Loading/Loading.stories.tsx Adds provider stories.
packages/atomic-elements/src/components/Feedback/Loading/Loading.context.ts Adds loading context and hook.
packages/atomic-elements/src/components/Feedback/Loading/Loading.component.tsx Adds loading provider.
packages/atomic-elements/src/components/Feedback/Loading/index.tsx Exports loading components.
packages/atomic-elements/src/components/Dropdowns/Select/__snapshots__/Select.spec.tsx.snap Updates snapshots.
packages/atomic-elements/src/components/Dropdowns/MultiSelect/MultiSelect.styles.ts Adds loading styles.
packages/atomic-elements/src/components/Dropdowns/MultiSelect/MultiSelect.stories.tsx Adds loading story.
packages/atomic-elements/src/components/Dropdowns/MultiSelect/MultiSelect.component.tsx Adds multiselect loading state.
packages/atomic-elements/src/components/Dropdowns/MultiSelect/__snapshots__/MultiSelect.spec.tsx.snap Updates snapshots.
packages/atomic-elements/src/components/Dropdowns/ListBox/__snapshots__/ListBox.spec.tsx.snap Updates snapshots.
packages/atomic-elements/src/components/Dropdowns/CustomSelect/CustomSelect.styles.ts Adds loading styles.
packages/atomic-elements/src/components/Dropdowns/CustomSelect/CustomSelect.stories.tsx Adds loading story.
packages/atomic-elements/src/components/Dropdowns/CustomSelect/CustomSelect.component.tsx Adds select loading state.
packages/atomic-elements/src/components/Dropdowns/CustomSelect/__snapshots__/CustomSelect.spec.tsx.snap Updates snapshots.
packages/atomic-elements/src/components/Dropdowns/Combobox/Combobox.styles.ts Adds loading styles.
packages/atomic-elements/src/components/Dropdowns/Combobox/Combobox.stories.tsx Adds loading story.
packages/atomic-elements/src/components/Dropdowns/Combobox/ComboBox.component.tsx Adds combobox loading state.
packages/atomic-elements/src/components/Dropdowns/Combobox/__snapshots__/Combobox.spec.tsx.snap Updates snapshots.
packages/atomic-elements/src/components/Content/Table/components/TableBody.tsx Integrates ambient table loading.
packages/atomic-elements/src/components/Chips/ChipGroup/__snapshots__/ChipGroup.spec.tsx.snap Updates snapshots.
.storybook/utils/helpers.tsx Adds loading Storybook controls.
.changeset/thick-tires-yawn.md Documents package changes.
Review details

Suppressed comments (2)

packages/atomic-elements/src/components/Feedback/Loading/Loading.component.tsx:37

  • When callers omit loadingLabel, the provider's live status announces an empty string even though the component has a translated fallback available. Use the resolved context value here so the ambient provider is accessible with its default label as well.
        <VisuallyHidden role="status" aria-live="polite">
          {isLoading ? loadingLabel : ""}
        </VisuallyHidden>

packages/atomic-elements/src/components/Inputs/FileInput/FileInput.component.tsx:86

  • When isLoading is true, this branch replaces the entire field contents with only the skeleton, so a FileInput label (and its accessible field context) disappears. This differs from the other loading field components and leaves the loading control without its normal label; keep the label/field wrapper rendered while replacing only the interactive input with the skeleton.
  • Files reviewed: 78/78 changed files
  • Comments generated: 2
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings September 17, 2026 16:42

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Changes recommended

The review found a critical label rendering regression and unresolved loading behavior issues.

Get a fresh assessment by requesting another Copilot review.

Review details

Suppressed comments (4)

packages/atomic-elements/src/components/Dropdowns/Combobox/ComboBox.component.tsx:60

  • The loading branch omits renderProps even though the normal branch applies it. This drops the aje-combobox, variant, size, custom class, and style output while loading, so the skeleton is not styled consistently with the same component when it is not loading.
      <StyledComboBoxField {...props} ref={ref}>

packages/atomic-elements/src/components/Feedback/Loading/Loading.component.tsx:23

  • The provider drops the caller's loadingLabel when constructing the context value and always publishes t("loading"). As a result, descendants using the ambient provider cannot receive a custom loading label even though LoadingProviderProps exposes it; pass loadingLabel ?? t("loading") into the context value (and keep the dependency aligned).
  const value = useMemo<LoadingProps>(
    () => ({ isLoading, loadingLabel: t("loading") }),
    [isLoading, loadingLabel]

packages/atomic-elements/src/components/Feedback/Loading/Loading.component.tsx:36

  • When no loadingLabel is passed, the provider's visually hidden status announces an empty string even though the component defines a translated default. Use the resolved label from the context value (or the same fallback) here so the ambient loading state is announced.
        <VisuallyHidden role="status" aria-live="polite">
          {isLoading ? loadingLabel : ""}

packages/atomic-elements/src/components/Inputs/FileInput/FileInput.component.tsx:86

  • The loading branch replaces the entire FileInput contents with only the skeleton, so the supplied label, required marker, message, and error are dropped (the Loading story passes label="Upload"). This makes the loading state lose the field's visible context and differs from the other field loading implementations that retain their label wrapper.
  • Files reviewed: 78/78 changed files
  • Comments generated: 1
  • Review effort level: Lite

Copilot AI review requested due to automatic review settings September 17, 2026 16:49

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔵 Needs a closer look

Two moderate issues remain in the loading provider label handling and FileInput loading structure.

Review details

Suppressed comments (2)

packages/atomic-elements/src/components/Feedback/Loading/Loading.component.tsx:24

  • The provider ignores the caller's loadingLabel when building the context and always publishes the translated default. As a result, a custom label passed to <Loading loadingLabel="..."> is announced by the provider but is not used by descendant skeletons/buttons, and when no label is supplied the provider's live region is empty because line 36 reads the un-fallbacked prop. Publish loadingLabel ?? t("loading") in both places (or derive the effective label once) so the ambient and live-region behavior agree.
  const value = useMemo<LoadingProps>(
    () => ({ isLoading, loadingLabel: t("loading") }),
    [isLoading, loadingLabel]
  );

packages/atomic-elements/src/components/Inputs/FileInput/FileInput.component.tsx:85

  • When loading, this early return removes the file input's label, message, error, and other field DOM props; a labeled <FileInput isLoading /> therefore renders only an unlabeled skeleton, unlike the normal branch and the other loading field components. Keep the field wrapper/label and status content in the loading branch, or render the skeleton through the same FileInputButton/field structure while replacing only the interactive control.
  • Files reviewed: 77/77 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants