Skip to content

meme submission modal ux#2136

Merged
ragnep merged 6 commits intomainfrom
meme-submission-modal-ux-improvements
Mar 19, 2026
Merged

meme submission modal ux#2136
ragnep merged 6 commits intomainfrom
meme-submission-modal-ux-improvements

Conversation

@ragnep
Copy link
Copy Markdown
Contributor

@ragnep ragnep commented Mar 18, 2026

Summary by CodeRabbit

  • New Features

    • Compact ("sm") field size option and visible required-field markers across submission forms.
    • File-type icons and grouped format chooser in upload UI; interactive hosting uses tabbed selection.
    • "Replace" upload control relabeled to "Change".
  • Improvements

    • Redesigned form layouts, responsive grids, spacing, and typography for clearer reading.
    • Enhanced validation visuals (success rings/checkmarks), refined buttons, labels, and preview/panel spacing.
  • Tests

    • Added/updated tests to verify required-marker rendering and updated styling behaviors.

Signed-off-by: ragnep <ragneinfo@gmail.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 18, 2026

Caution

Review failed

Pull request was closed or merged during review

📝 Walkthrough

Walkthrough

Threads a new size variant and required-marker support through trait components, refactors section-aware form layouts, updates many Tailwind classnames and layout structures across submission UI (modals, panels, file upload, preview), and adds/updates tests to assert the new visual states.

Changes

Cohort / File(s) Summary
Modal & Container
components/waves/memes/MemesArtSubmissionModal.tsx, components/waves/memes/submission/MemesArtSubmissionContainer.tsx
Constrain modal width and simplify container/header layout and heading typography; minor class changes only.
Trait Renderer & Steps
components/waves/memes/MemesArtSubmissionTraits.tsx, components/waves/memes/submission/steps/ArtworkStep.tsx, components/waves/memes/submission/steps/AdditionalInfoStep.tsx, components/waves/memes/submission/steps/AgreementStep.tsx
Refactor to section-aware field layouts; propagate showRequiredMarkers and size into step components; update paddings, button usage, and some validation checks.
Artwork Details & Preview
components/waves/memes/submission/details/ArtworkDetails.tsx, components/waves/memes/submission/preview/MemesSubmissionPreviewScreen.tsx
Add size/showRequiredMarkers, centralize field-state class helpers, adjust input/textarea sizing and required marker rendering, and add top padding to preview.
Trait Core & Wrapper
components/waves/memes/traits/TraitField.tsx, components/waves/memes/traits/TraitWrapper.tsx, components/waves/memes/traits/Section.tsx
Add size and required-marker props, update label/adornment handling, checkmark sizing/placement, header decoration simplification, and extend memo equality checks.
Individual Trait Controls
components/waves/memes/traits/TextTrait.tsx, .../NumberTrait.tsx, .../DropdownTrait.tsx, .../BooleanTrait.tsx
Propagate size/showRequiredMarker to controls; make padding/typography/ring visuals size-aware; Dropdown adds imperative visual sync and success icon; Boolean button sizing adjusted.
File Upload & Provider UI
components/waves/memes/MemesArtSubmissionFile.tsx, components/waves/memes/file-upload/components/FilePreview.tsx, .../FileTypeIndicator.tsx, .../UploadArea.tsx
Switch format list → grouped kind/label model, add icons for file kinds, replace TabToggle with CommonTabs, tweak upload/interactive panel layout and overflow, and update preview button label.
Submission UI Constants
constants/submission-media.constants.ts
Replace SUBMISSION_UI_FORMAT_CATEGORIES with typed SUBMISSION_UI_FORMAT_GROUPS mapping SubmissionMediaCategory → display label.
Config & Small Components
components/waves/memes/submission/components/AdditionalMediaUpload.tsx, AirdropConfig.tsx, AllowlistBatchManager.tsx, PaymentConfig.tsx, FormSection.tsx
Remove literal * from labels in favor of showRequiredMarker, adjust label tones, spacing, input sizing, and some state normalization changes.
Tests
__tests__/components/waves/memes/.../*.test.tsx
Update/add tests for required-marker rendering and new success/filled styling for Text/Dropdown/ArtworkDetails and TraitWrapper; adjust selectors and add visual assertions.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • prxt6529

Poem

🐰 A tiny hop, a tidy prop,
Fields mark stars where labels crop,
Small or default, grids align,
Tabs and icons, borders fine,
A rabbit twitches—layout done, hip-hop! 🎉

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title "meme submission modal ux" directly corresponds to the changeset's primary focus: comprehensive UX improvements across the meme submission modal component, including layout refinements, form field styling, required marker indicators, and responsive design adjustments.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch meme-submission-modal-ux-improvements
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (5)
components/waves/memes/MemesArtSubmissionTraits.tsx (1)

105-165: Consider extracting section titles as constants.

The section-specific rendering logic relies on string comparisons ("Basic Information", "Card Points", "Card Attributes"). If section titles are changed in getFormSections, the layout logic here will silently fall back to the default grid without warning.

🔧 Suggested improvement: use constants for section titles
// In schema.ts or a shared constants file
export const SECTION_TITLES = {
  BASIC_INFO: "Basic Information",
  CARD_POINTS: "Card Points", 
  CARD_ATTRIBUTES: "Card Attributes",
} as const;

Then use these constants in both getFormSections and this component to ensure consistency.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/waves/memes/MemesArtSubmissionTraits.tsx` around lines 105 - 165,
This code compares literal section.title strings ("Basic Information", "Card
Points", "Card Attributes") inside the render logic, which can break silently if
getFormSections changes; refactor by extracting those literals into shared
constants (e.g., SECTION_TITLES with keys BASIC_INFO, CARD_POINTS,
CARD_ATTRIBUTES) and replace the string comparisons in this file (the if checks
against section.title) and in getFormSections to use those constants; update any
imports and ensure renderField and Section usage remains unchanged so layout
behavior is preserved.
components/waves/memes/traits/BooleanTrait.tsx (1)

117-125: Missing size prop for consistency with other trait components.

TextTrait, NumberTrait, and DropdownTrait all accept and forward a size prop to TraitWrapper, but BooleanTrait does not. This inconsistency means BooleanTrait won't respond to size changes when used alongside other traits.

The TraitWrapper already accepts a size prop (as shown in the relevant code snippet), so this component should likely support it as well for a consistent API.

🔧 Suggested fix: add size prop support
 type BooleanTraitProps = {
   readonly label: string;
   readonly field: keyof TraitsData;
   readonly className?: string | undefined;
   readonly error?: string | null | undefined;
   readonly onBlur?: ((field: keyof TraitsData) => void) | undefined;
   readonly traits: TraitsData;
   readonly updateBoolean: (field: keyof TraitsData, value: boolean) => void;
+  readonly size?: "default" | "sm" | undefined;
 };

 export const BooleanTrait: React.FC<BooleanTraitProps> = React.memo(
-  ({ label, field, traits, updateBoolean, className, error, onBlur }) => {
+  ({ label, field, traits, updateBoolean, className, error, onBlur, size = "default" }) => {
     // ...
     return (
       <TraitWrapper
         label={label}
         isBoolean={true}
         className={className}
         error={error}
         id={`field-${field}`}
         isFieldFilled={isFieldFilled}
+        size={size}
       >
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/waves/memes/traits/BooleanTrait.tsx` around lines 117 - 125,
BooleanTrait is not forwarding the size prop to TraitWrapper, causing
inconsistent behavior with TextTrait/NumberTrait/DropdownTrait; update the
BooleanTrait component to accept a size prop (same type/name used by the other
traits) and pass it into the TraitWrapper JSX (add size={size} to the
TraitWrapper props and include size in BooleanTrait's props
destructuring/definition) so the component responds to size changes consistently
with TextTrait, NumberTrait, and DropdownTrait.
components/waves/memes/traits/Section.tsx (1)

32-41: Custom equality function excludes children from comparison.

The comment states "React handles children comparison internally," but this is incorrect when using a custom comparator with React.memo. React does not automatically compare children when you provide a custom equality function—it only uses your function.

If only the children prop changes (while title and className remain the same), this component will skip re-rendering, potentially displaying stale content.

In practice, this may not cause issues if the parent always re-renders when children need to update, but the comment is misleading.

🔧 Suggested fix: either remove the custom comparator or include children

Option 1 - Remove custom comparator (simpler, React's default shallow comparison handles this):

-export const Section = memo(SectionComponent, areSectionPropsEqual);
+export const Section = memo(SectionComponent);

Option 2 - Update the comment to clarify the intentional behavior:

-    // React handles children comparison internally
+    // Children comparison intentionally skipped - parent re-renders will propagate changes
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/waves/memes/traits/Section.tsx` around lines 32 - 41, The custom
comparator areSectionPropsEqual in Section.tsx incorrectly omits children
comparison so React.memo will skip rerenders when only children change; fix by
either removing the custom comparator so React.memo uses its default shallow
prop comparison, or update areSectionPropsEqual to also compare
prevProps.children !== nextProps.children (or a deep/shallow comparison
appropriate for your children) and update the inline comment to reflect that
children are being checked; reference the SectionProps shape, the
areSectionPropsEqual function, and the title/className comparisons when making
the change.
components/waves/memes/submission/details/ArtworkDetails.tsx (1)

115-119: Consider extracting repeated size class fragments.

The same size === "sm" class logic is duplicated for both label and control blocks; a small helper/constant would reduce drift.

Also applies to: 137-141, 160-164, 184-188

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/waves/memes/submission/details/ArtworkDetails.tsx` around lines
115 - 119, The repeated size-dependent class expression (size === "sm" ?
"tw-text-[11px]" : "tw-text-xs") in ArtworkDetails is duplicated across multiple
className usages (label and control blocks around the size checks at the shown
diff and also at the blocks referenced 137-141, 160-164, 184-188); extract that
expression into a small local constant or helper (e.g., sizeTextClass or
getSizeTextClass(size)) at the top of the ArtworkDetails component and replace
each inline ternary with that constant to remove duplication and keep behavior
identical.
components/waves/memes/traits/TraitWrapper.tsx (1)

65-65: Add explicit required property to field definitions instead of coupling to !readOnly.

The required indicator (*) at line 65 is shown for all editable fields because it's tied to !readOnly. However, FieldDefinition has no required property to distinguish between required and optional editable fields. If optional trait fields need to be added, the UI and validation logic cannot represent them without refactoring. Add a required property to BaseFieldDefinition and update the indicator logic and validation rules accordingly.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/waves/memes/traits/TraitWrapper.tsx` at line 65, Add an explicit
required flag to the field model: extend BaseFieldDefinition (and any
FieldDefinition union types) with a required?: boolean (default false) so traits
can declare required vs optional; in TraitWrapper.tsx replace the current
indicator condition {!readOnly && <span...>} with a check against field.required
(e.g., show the red * only when field.required is true) and update any
validation logic/functions that currently infer requiredness from !readOnly to
instead read field.required (ensure creation/edit paths set required
appropriately when constructing field definitions).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@components/waves/memes/traits/NumberTrait.tsx`:
- Around line 169-173: The isFieldFilled useMemo currently treats n === 0 as not
filled which incorrectly rejects valid zero values; in the isFieldFilled logic
(inside the useMemo that parses currentInputValue with Number.parseFloat) remove
the explicit n === 0 check so zero is considered filled, and keep the other
guards (Number.isFinite(n), min/max comparisons) unchanged so empty/NaN and
out-of-range values still return false.

---

Nitpick comments:
In `@components/waves/memes/MemesArtSubmissionTraits.tsx`:
- Around line 105-165: This code compares literal section.title strings ("Basic
Information", "Card Points", "Card Attributes") inside the render logic, which
can break silently if getFormSections changes; refactor by extracting those
literals into shared constants (e.g., SECTION_TITLES with keys BASIC_INFO,
CARD_POINTS, CARD_ATTRIBUTES) and replace the string comparisons in this file
(the if checks against section.title) and in getFormSections to use those
constants; update any imports and ensure renderField and Section usage remains
unchanged so layout behavior is preserved.

In `@components/waves/memes/submission/details/ArtworkDetails.tsx`:
- Around line 115-119: The repeated size-dependent class expression (size ===
"sm" ? "tw-text-[11px]" : "tw-text-xs") in ArtworkDetails is duplicated across
multiple className usages (label and control blocks around the size checks at
the shown diff and also at the blocks referenced 137-141, 160-164, 184-188);
extract that expression into a small local constant or helper (e.g.,
sizeTextClass or getSizeTextClass(size)) at the top of the ArtworkDetails
component and replace each inline ternary with that constant to remove
duplication and keep behavior identical.

In `@components/waves/memes/traits/BooleanTrait.tsx`:
- Around line 117-125: BooleanTrait is not forwarding the size prop to
TraitWrapper, causing inconsistent behavior with
TextTrait/NumberTrait/DropdownTrait; update the BooleanTrait component to accept
a size prop (same type/name used by the other traits) and pass it into the
TraitWrapper JSX (add size={size} to the TraitWrapper props and include size in
BooleanTrait's props destructuring/definition) so the component responds to size
changes consistently with TextTrait, NumberTrait, and DropdownTrait.

In `@components/waves/memes/traits/Section.tsx`:
- Around line 32-41: The custom comparator areSectionPropsEqual in Section.tsx
incorrectly omits children comparison so React.memo will skip rerenders when
only children change; fix by either removing the custom comparator so React.memo
uses its default shallow prop comparison, or update areSectionPropsEqual to also
compare prevProps.children !== nextProps.children (or a deep/shallow comparison
appropriate for your children) and update the inline comment to reflect that
children are being checked; reference the SectionProps shape, the
areSectionPropsEqual function, and the title/className comparisons when making
the change.

In `@components/waves/memes/traits/TraitWrapper.tsx`:
- Line 65: Add an explicit required flag to the field model: extend
BaseFieldDefinition (and any FieldDefinition union types) with a required?:
boolean (default false) so traits can declare required vs optional; in
TraitWrapper.tsx replace the current indicator condition {!readOnly &&
<span...>} with a check against field.required (e.g., show the red * only when
field.required is true) and update any validation logic/functions that currently
infer requiredness from !readOnly to instead read field.required (ensure
creation/edit paths set required appropriately when constructing field
definitions).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0b773e2b-6192-4e96-bd08-8b3c98c607c7

📥 Commits

Reviewing files that changed from the base of the PR and between 25a3edc and 9fef9b0.

📒 Files selected for processing (12)
  • components/waves/memes/MemesArtSubmissionModal.tsx
  • components/waves/memes/MemesArtSubmissionTraits.tsx
  • components/waves/memes/submission/MemesArtSubmissionContainer.tsx
  • components/waves/memes/submission/details/ArtworkDetails.tsx
  • components/waves/memes/submission/steps/ArtworkStep.tsx
  • components/waves/memes/traits/BooleanTrait.tsx
  • components/waves/memes/traits/DropdownTrait.tsx
  • components/waves/memes/traits/NumberTrait.tsx
  • components/waves/memes/traits/Section.tsx
  • components/waves/memes/traits/TextTrait.tsx
  • components/waves/memes/traits/TraitField.tsx
  • components/waves/memes/traits/TraitWrapper.tsx

Comment thread components/waves/memes/traits/NumberTrait.tsx
ragnep added 4 commits March 19, 2026 12:44
Signed-off-by: ragnep <ragneinfo@gmail.com>
Signed-off-by: ragnep <ragneinfo@gmail.com>
Signed-off-by: ragnep <ragneinfo@gmail.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (6)
components/waves/memes/submission/MemesArtSubmissionContainer.tsx (1)

305-305: Remove no-op responsive border utility on the header row.

On Line 305, lg:tw-border-b-0 has no effect because this element never sets tw-border-b (the border-bottom is on the parent div at Line 303). Removing it reduces class noise.

Proposed diff
-              className="tw-flex tw-w-full tw-items-center tw-flex-shrink-0 tw-justify-between tw-pt-6 lg:tw-border-b-0"
+              className="tw-flex tw-w-full tw-items-center tw-flex-shrink-0 tw-justify-between tw-pt-6"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/waves/memes/submission/MemesArtSubmissionContainer.tsx` at line
305, In the MemesArtSubmissionContainer component remove the redundant
responsive utility from the header row: delete the `lg:tw-border-b-0` token from
the className string on the header element (the element with className starting
"tw-flex tw-w-full tw-items-center tw-flex-shrink-0 tw-justify-between
tw-pt-6"); this simply eliminates a no-op Tailwind class since the border-bottom
is applied on the parent and not this element.
constants/submission-media.constants.ts (1)

83-87: Consider whether the "GLB" label for interactive fully represents supported formats.

The interactive category in SUBMISSION_MEDIA_TYPES includes GLB, GLTF, and HTML formats, but the UI label only shows "GLB". If this is intentional to keep the UI simple, this is fine. Otherwise, consider updating the label to reflect all supported interactive formats.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@constants/submission-media.constants.ts` around lines 83 - 87, The UI label
for the interactive group is currently "GLB" but the interactive category (see
SUBMISSION_MEDIA_TYPES and the SUBMISSION_UI_FORMAT_GROUPS constant) actually
supports GLB, GLTF, and HTML; update the label in SUBMISSION_UI_FORMAT_GROUPS
(the entry with kind: "interactive") to either list all supported formats (e.g.,
"GLB, GLTF, HTML") or a more generic term like "INTERACTIVE" to accurately
reflect supported types, ensuring the displayed text matches the formats defined
in SUBMISSION_MEDIA_TYPES.
components/waves/memes/traits/TraitField.tsx (1)

92-103: BooleanTrait doesn't receive showRequiredMarker or size props.

Unlike TextTrait, NumberTrait, and DropdownTrait, BooleanTrait doesn't receive the new props. If this is intentional (e.g., boolean fields have fixed sizing), a brief comment would clarify this design decision. Otherwise, consider whether BooleanTrait should also support these props for consistency.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/waves/memes/traits/TraitField.tsx` around lines 92 - 103, The
BooleanTrait rendering in TraitField.tsx omits the new props showRequiredMarker
and size that TextTrait, NumberTrait and DropdownTrait receive; either pass
showRequiredMarker={showRequiredMarker} and size={size} into the <BooleanTrait
... /> JSX so BooleanTrait gets consistent behavior, or if omission is
intentional, add a brief comment above the BooleanTrait return explaining that
booleans have fixed sizing and do not support showRequiredMarker/size; update
the BooleanTrait prop type (component) if you choose to support the props.
components/waves/memes/submission/ui/FormSection.tsx (1)

28-28: Behavior change for falsy headerRight values.

The change from headerRight && ... to headerRight !== undefined now renders the wrapper <div> for falsy values like null, 0, "", or false. If headerRight={null} is explicitly passed, an empty <div> will render, which may affect layout spacing.

If preserving space for falsy content is intentional, consider adding a brief comment. Otherwise, the previous truthiness check was safer.

Alternative: Check for both undefined and null
-        {headerRight !== undefined && <div>{headerRight}</div>}
+        {headerRight != null && <div>{headerRight}</div>}

This excludes both undefined and null while still allowing 0 or false if needed.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/waves/memes/submission/ui/FormSection.tsx` at line 28, The
conditional in FormSection.tsx now uses headerRight !== undefined which causes a
wrapper <div> to render for other falsy values (null, 0, "", false); revert to a
truthiness check or explicitly exclude null as well to avoid rendering an empty
wrapper when headerRight is null—update the condition around the headerRight
render in the FormSection component to either use headerRight &&
<div>{headerRight}</div> or use an explicit null/undefined guard (e.g.,
headerRight != null) depending on whether you want to allow 0/false as valid
content, and add a short comment clarifying the chosen behavior.
__tests__/components/waves/memes/traits/DropdownTrait.test.tsx (1)

78-87: Hardcoded RGB values make tests brittle.

Asserting exact RGB values (rgb(132, 132, 144), rgb(239, 239, 241)) tightly couples tests to specific color implementations. Consider asserting on the presence/absence of style properties or CSS variables instead, which would be more maintainable if design tokens change.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@__tests__/components/waves/memes/traits/DropdownTrait.test.tsx` around lines
78 - 87, The test is brittle because it asserts exact RGB strings for the select
element color; update the assertions around select and iconContainer (used with
fireEvent.change and expect(...).toHaveClass) to check semantics instead of
hardcoded colors — e.g., assert that (select as HTMLSelectElement).style.color
is truthy/empty or that the element uses the expected CSS variable/class (check
computedStyle.getPropertyValue('--your-color-var') or presence/absence of a
modifier class) before and after fireEvent.change(select, { target: { value:
"Rare" } }) so the test validates behavior without relying on exact RGB values.
components/waves/memes/MemesArtSubmissionTraits.tsx (1)

79-81: Use stable section ids for layout branching.

The "Basic Information", "Card Points", and "Card Attributes" checks make rendering depend on display copy from getFormSections. A title rename/localization would silently change both the chosen layout and the sectionKey. A schema-level id/layout field would keep this stable.

Also applies to: 108-156

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/waves/memes/MemesArtSubmissionTraits.tsx` around lines 79 - 81,
The section key and layout branching currently rely on display copy
(section.title and field names) in the sectionKey calculation and downstream
checks, which is brittle; update the code to prefer a stable schema identifier
(e.g., section.id or section.layout) when available and fall back to the old
logic only if that id is missing. Specifically, change uses in
MemesArtSubmissionTraits (the sectionKey computation and any conditional checks
for "Basic Information", "Card Points", "Card Attributes") to first read
section.id or section.layout (e.g., section.id || section.layout ||
previousTitleFallback) and update the layout-branching conditions to match those
stable ids instead of the display titles; apply the same change to the other
occurrences referenced (lines ~108-156) so rendering and keys are resilient to
title changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@__tests__/components/waves/memes/submission/details/ArtworkDetails.test.tsx`:
- Around line 63-68: The test assertions in ArtworkDetails.test.tsx expect the
filled input class 'tw-ring-emerald-500/30' but the components apply
'tw-ring-emerald-600/45'; update the expectations in the test (the two
expect(...) checks that use screen.getByLabelText(/Artwork Title/) and
screen.getByLabelText(/Description/)) to assert for 'tw-ring-emerald-600/45' so
they match the implementation in ArtworkDetails (line ~27) and TextTrait (line
~117).

In `@__tests__/components/waves/memes/traits/TextTrait.test.tsx`:
- Line 45: The test in TextTrait.test.tsx is asserting the wrong ring class;
update the expectation to match the component's filled-state class used in
TextTrait.tsx (the TextTrait component applies "tw-ring-emerald-600/45" for the
filled state), so change the assertion from
expect(input).toHaveClass("tw-ring-emerald-500/30") to
expect(input).toHaveClass("tw-ring-emerald-600/45") (or adjust the component if
you intend the other value), referencing the TextTrait component's filled-state
logic to keep the test and implementation in sync.

In `@components/waves/memes/MemesArtSubmissionFile.tsx`:
- Around line 174-195: providerOptions is typed as
CommonSelectItem<InteractiveMediaProvider> but sets value: provider.key
(string), so handleProviderSelect (which expects InteractiveMediaProvider)
receives a string; either make the select value the provider object or change
types/callbacks to use string keys. Fix by updating providerOptions mapping to
use value: provider (not provider.key) and ensure INTERACTIVE_MEDIA_PROVIDERS
items are the InteractiveMediaProvider shape so CommonTabsTab will call
setSelected with the object, or alternatively change the generic on
providerOptions to CommonSelectItem<string>[] and adjust handleProviderSelect
signature to (providerKey: string) and call onExternalProviderChange with the
providerKey (or look up the object from INTERACTIVE_MEDIA_PROVIDERS before
comparing with externalProvider). Ensure references: providerOptions,
handleProviderSelect, INTERACTIVE_MEDIA_PROVIDERS, externalProvider,
onExternalProviderChange, CommonSelectItem, CommonTabsTab are updated
consistently.

In `@components/waves/memes/submission/components/AdditionalMediaUpload.tsx`:
- Around line 311-327: The textareas inside the TraitWrapper (e.g., the one
bound to aboutArtist with value={aboutArtist} and onChange calling
onAboutArtistChange) are visually marked required but lack semantic required
attributes; add aria-required="true" (or required if you want native browser
validation) to these textarea elements and do the same for the other textarea(s)
noted (the block around lines 333-349) so screen readers announce the required
constraint and accessibility matches the step validation; keep existing
aria/error handling (errors?.aboutArtist) intact.

In `@components/waves/memes/submission/components/AirdropConfig.tsx`:
- Around line 178-185: The required-star is shown unconditionally on each
TraitWrapper in AirdropConfig (prop showRequiredMarker=true) which contradicts
the validation rules: an address is required only when count > 0 (and count is
required only when an address exists) in AdditionalInfo logic; update the
rendering in AirdropConfig to compute a boolean like isActiveRow (e.g., based on
the corresponding row's count > 0 or existing address for the given index) and
pass that to TraitWrapper's showRequiredMarker so the star appears only for
active/validated rows (also update the other occurrence around lines 201-207 the
same way).

---

Nitpick comments:
In `@__tests__/components/waves/memes/traits/DropdownTrait.test.tsx`:
- Around line 78-87: The test is brittle because it asserts exact RGB strings
for the select element color; update the assertions around select and
iconContainer (used with fireEvent.change and expect(...).toHaveClass) to check
semantics instead of hardcoded colors — e.g., assert that (select as
HTMLSelectElement).style.color is truthy/empty or that the element uses the
expected CSS variable/class (check
computedStyle.getPropertyValue('--your-color-var') or presence/absence of a
modifier class) before and after fireEvent.change(select, { target: { value:
"Rare" } }) so the test validates behavior without relying on exact RGB values.

In `@components/waves/memes/MemesArtSubmissionTraits.tsx`:
- Around line 79-81: The section key and layout branching currently rely on
display copy (section.title and field names) in the sectionKey calculation and
downstream checks, which is brittle; update the code to prefer a stable schema
identifier (e.g., section.id or section.layout) when available and fall back to
the old logic only if that id is missing. Specifically, change uses in
MemesArtSubmissionTraits (the sectionKey computation and any conditional checks
for "Basic Information", "Card Points", "Card Attributes") to first read
section.id or section.layout (e.g., section.id || section.layout ||
previousTitleFallback) and update the layout-branching conditions to match those
stable ids instead of the display titles; apply the same change to the other
occurrences referenced (lines ~108-156) so rendering and keys are resilient to
title changes.

In `@components/waves/memes/submission/MemesArtSubmissionContainer.tsx`:
- Line 305: In the MemesArtSubmissionContainer component remove the redundant
responsive utility from the header row: delete the `lg:tw-border-b-0` token from
the className string on the header element (the element with className starting
"tw-flex tw-w-full tw-items-center tw-flex-shrink-0 tw-justify-between
tw-pt-6"); this simply eliminates a no-op Tailwind class since the border-bottom
is applied on the parent and not this element.

In `@components/waves/memes/submission/ui/FormSection.tsx`:
- Line 28: The conditional in FormSection.tsx now uses headerRight !== undefined
which causes a wrapper <div> to render for other falsy values (null, 0, "",
false); revert to a truthiness check or explicitly exclude null as well to avoid
rendering an empty wrapper when headerRight is null—update the condition around
the headerRight render in the FormSection component to either use headerRight &&
<div>{headerRight}</div> or use an explicit null/undefined guard (e.g.,
headerRight != null) depending on whether you want to allow 0/false as valid
content, and add a short comment clarifying the chosen behavior.

In `@components/waves/memes/traits/TraitField.tsx`:
- Around line 92-103: The BooleanTrait rendering in TraitField.tsx omits the new
props showRequiredMarker and size that TextTrait, NumberTrait and DropdownTrait
receive; either pass showRequiredMarker={showRequiredMarker} and size={size}
into the <BooleanTrait ... /> JSX so BooleanTrait gets consistent behavior, or
if omission is intentional, add a brief comment above the BooleanTrait return
explaining that booleans have fixed sizing and do not support
showRequiredMarker/size; update the BooleanTrait prop type (component) if you
choose to support the props.

In `@constants/submission-media.constants.ts`:
- Around line 83-87: The UI label for the interactive group is currently "GLB"
but the interactive category (see SUBMISSION_MEDIA_TYPES and the
SUBMISSION_UI_FORMAT_GROUPS constant) actually supports GLB, GLTF, and HTML;
update the label in SUBMISSION_UI_FORMAT_GROUPS (the entry with kind:
"interactive") to either list all supported formats (e.g., "GLB, GLTF, HTML") or
a more generic term like "INTERACTIVE" to accurately reflect supported types,
ensuring the displayed text matches the formats defined in
SUBMISSION_MEDIA_TYPES.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1ba803fe-88f4-4b15-9a73-f59d2b38ce44

📥 Commits

Reviewing files that changed from the base of the PR and between 9fef9b0 and ecdbb3f.

📒 Files selected for processing (27)
  • __tests__/components/waves/memes/submission/details/ArtworkDetails.test.tsx
  • __tests__/components/waves/memes/traits/DropdownTrait.test.tsx
  • __tests__/components/waves/memes/traits/TextTrait.test.tsx
  • __tests__/components/waves/memes/traits/TraitWrapper.test.tsx
  • components/waves/memes/MemesArtSubmissionFile.tsx
  • components/waves/memes/MemesArtSubmissionTraits.tsx
  • components/waves/memes/file-upload/components/FilePreview.tsx
  • components/waves/memes/file-upload/components/FileTypeIndicator.tsx
  • components/waves/memes/file-upload/components/UploadArea.tsx
  • components/waves/memes/submission/MemesArtSubmissionContainer.tsx
  • components/waves/memes/submission/components/AdditionalMediaUpload.tsx
  • components/waves/memes/submission/components/AirdropConfig.tsx
  • components/waves/memes/submission/components/AllowlistBatchManager.tsx
  • components/waves/memes/submission/components/PaymentConfig.tsx
  • components/waves/memes/submission/details/ArtworkDetails.tsx
  • components/waves/memes/submission/preview/MemesSubmissionPreviewScreen.tsx
  • components/waves/memes/submission/steps/AdditionalInfoStep.tsx
  • components/waves/memes/submission/steps/AgreementStep.tsx
  • components/waves/memes/submission/steps/ArtworkStep.tsx
  • components/waves/memes/submission/ui/FormSection.tsx
  • components/waves/memes/traits/DropdownTrait.tsx
  • components/waves/memes/traits/NumberTrait.tsx
  • components/waves/memes/traits/Section.tsx
  • components/waves/memes/traits/TextTrait.tsx
  • components/waves/memes/traits/TraitField.tsx
  • components/waves/memes/traits/TraitWrapper.tsx
  • constants/submission-media.constants.ts
✅ Files skipped from review due to trivial changes (3)
  • components/waves/memes/submission/preview/MemesSubmissionPreviewScreen.tsx
  • components/waves/memes/file-upload/components/FilePreview.tsx
  • components/waves/memes/submission/components/AllowlistBatchManager.tsx
🚧 Files skipped from review as they are similar to previous changes (5)
  • components/waves/memes/submission/details/ArtworkDetails.tsx
  • components/waves/memes/traits/DropdownTrait.tsx
  • components/waves/memes/submission/steps/ArtworkStep.tsx
  • components/waves/memes/traits/TraitWrapper.tsx
  • components/waves/memes/traits/NumberTrait.tsx

Comment thread __tests__/components/waves/memes/traits/TextTrait.test.tsx
Comment thread components/waves/memes/MemesArtSubmissionFile.tsx
Comment thread components/waves/memes/submission/components/AirdropConfig.tsx
Signed-off-by: ragnep <ragneinfo@gmail.com>
@sonarqubecloud
Copy link
Copy Markdown

@ragnep ragnep merged commit c7171f7 into main Mar 19, 2026
6 of 7 checks passed
@ragnep ragnep deleted the meme-submission-modal-ux-improvements branch March 19, 2026 13:07
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.

2 participants