reorder web home creation type priorities (#6892)

* fix web home media pill placement

* swap UI Mockup and Slide deck positions
This commit is contained in:
Ray Xi
2026-08-14 09:02:56 +00:00
committed by GitHub
parent 8b452355b7
commit 7b7214e52a
4 changed files with 26 additions and 16 deletions
@@ -1,7 +1,7 @@
// Capsule type row — the pill replacement for the fanned type carousel
// (per product: 12 个创作类型全部换成胶囊形). ONE line exactly as wide as the
// (per product: all 12 creation types use capsules). ONE line exactly as wide as the
// composer card below it, EVERY gap identical (8px): the pills that fit
// render inline, then the pinned 视频 pill, then the 全部 button, and the
// render inline, then the pinned Image pill, then the All button, and the
// rest fold into the 全部 popover. Fit is computed against an invisible
// probe row that always lays out the full set at natural size, so showing /
// hiding pills can never feed back into its own measurement. Selection
@@ -15,10 +15,10 @@ import { useT } from '../../i18n';
// today, so this only guards against future catalog growth widening the row.
const MAX_PILLS = 12;
// Pills pinned just BEFORE the 全部 trigger (per product: 视频 stays visible
// Pills pinned just BEFORE the All trigger (per product: Image stays visible
// in the row, never folded into the popover). Pinned pills sit outside the
// fit computation's flowing run, so they survive any window width.
const PINNED_PILL_IDS: readonly string[] = ['video'];
const PINNED_PILL_IDS: readonly string[] = ['image'];
// Must match .home-hero__type-pills-wrap's CSS gap.
const PILL_GAP = 8;
+7 -8
View File
@@ -381,19 +381,18 @@ export function chipsForGroup(group: ChipGroup): HomeHeroChip[] {
}
// Display order for the inline `create` scenario rail. The composer leads with
// Website clone (the fastest "paste a URL, get a site" on-ramp), then the slide
// deck ("Slides") and the core build scenarios in decreasing generality
// (Prototype → Wireframe → Mobile → Document → Animation), then the media
// UI Mockup, followed by Slide deck and the remaining core build scenarios
// (Wireframe → Mobile → Document → Animation), then the media
// scenarios. Brand Kit is intentionally omitted here so it trails the scenario
// set — it dispatches into the Brand Kit tab rather than seeding a scenario
// plugin. Any create chip not listed keeps its catalog order after the explicit
// entries (see `orderedCreateChips`).
export const CREATE_RAIL_ORDER = [
// Slide deck leads (per product: 幻灯片 is the row's first, default-selected
// type); Website clone trails the whole list so at typical widths it lives
// in the 全部 overflow popover rather than the visible pill row.
'deck',
// UI Mockup leads and Slide deck follows. Website clone trails the whole list
// so at typical widths it lives in the All overflow popover rather than the
// visible pill row.
'prototype',
'deck',
'wireframe',
'mobile',
'document',
@@ -425,7 +424,7 @@ export const ONBOARDING_ARTIFACT_CHIP_IDS = CREATE_RAIL_ORDER.filter(
// The `create` chips in rail-display order. Listed ids come first in
// `CREATE_RAIL_ORDER`; any unlisted create chip (e.g. `create-brand-kit`)
// trails in catalog order. Reordering through this helper keeps the catalog
// data table stable while letting the rail lead with the slide deck.
// data table stable while letting the rail lead with UI Mockup.
export function orderedCreateChips(): HomeHeroChip[] {
const create = chipsForGroup('create');
const listed = CREATE_RAIL_ORDER
@@ -88,10 +88,10 @@ describe('HomeHero scenario cards', () => {
).toContain('Slide deck');
});
it('leads the create rail with the slide deck and trails Website clone', () => {
it('leads the create rail with UI Mockup, then Slide deck, and trails Website clone', () => {
const ordered = orderedCreateChips();
const ids = ordered.map((chip) => chip.id);
expect(ids[0]).toBe('deck');
expect(ids.slice(0, 2)).toEqual(['prototype', 'deck']);
// Website clone sits behind every other explicit rail type (only the
// unlisted catalog tail, e.g. Brand Kit, follows), so the visible pill
// row overflows it into the 全部 popover at typical widths.
@@ -1,6 +1,6 @@
// @vitest-environment jsdom
import { act, cleanup, render, screen } from '@testing-library/react';
import { act, cleanup, fireEvent, render, screen } from '@testing-library/react';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { TypePillRow } from '../../../src/components/home-hero/TypePillRow';
@@ -9,7 +9,7 @@ import {
type HomeHeroChip,
} from '../../../src/components/home-hero/chips';
const chips = ['deck', 'prototype', 'wireframe', 'video'].map((chipId) => {
const chips = ['deck', 'prototype', 'wireframe', 'image', 'video'].map((chipId) => {
const chip = HOME_HERO_CHIPS.find((candidate) => candidate.id === chipId);
if (!chip) throw new Error(`Missing chip fixture: ${chipId}`);
return chip;
@@ -62,6 +62,17 @@ function renderPillRow(labelFor: (chipId: string) => string) {
}
describe('TypePillRow', () => {
it('keeps Image inline and moves Video into All when the row overflows', () => {
renderPillRow((chipId) => chipId);
expect(screen.queryByTestId('home-hero-type-pill-image')).not.toBeNull();
expect(screen.queryByTestId('home-hero-type-pill-video')).toBeNull();
fireEvent.click(screen.getByTestId('home-hero-type-pills-more'));
expect(screen.queryByTestId('home-hero-type-pill-video-more')).not.toBeNull();
expect(screen.queryByTestId('home-hero-type-pill-image-more')).toBeNull();
});
it('recomputes the inline split when rendered labels change without resizing the container', () => {
const labels = new Map(chips.map((chip) => [chip.id, chip.label]));
const view = renderPillRow((chipId) => labels.get(chipId) ?? chipId);