Stress test: MemberRow and Header
I built no toggle and wrote no files, as you asked. I didn't render anything either, so every finding below is inferred from the JSX. I couldn't see your CSS for .avatar, .name, .email, .role, .badge or .menu, nor the parent list. Any finding that depends on those is marked.
Surface map
| Field | Source | Limit | Optional? |
|---|---|---|---|
name |
member.name |
255 | No |
email |
member.email |
254 | No |
title |
member.title |
120 | Yes (|| '-') |
status |
member.status |
Enum values unknown | No |
avatar |
member.avatar |
Unknown | Probably, since there's no fallback |
count |
Header prop |
Unbounded | No |
| Member list | Parent (not shown) | Unbounded | Can be empty |
Part 1: What broke
| # | Severity | Field | Worst-case value | What happens | Fix |
|---|---|---|---|---|---|
| 1 | Broken | name |
'Jo', 'Madonna', '张伟', '' |
name.split(' ')[1] is undefined, so undefined[0] throws a TypeError. The row crashes during render. Without an error boundary, the whole member list unmounts. One-word names and Chinese or Japanese names (which have no spaces) are common. The initials variable is never used, so the crash buys nothing. |
Delete the line, or replace it with a safe helper that takes the first and last word, uses Intl.Segmenter for graphemes and falls back to an icon. Then use it as the avatar fallback (see #4). |
| 2 | Broken | email |
254-char email, or a realistic [email protected] |
An email has no break opportunities. The text column is a flex child with min-width: auto, so it can't shrink below the string's width. It pushes the badge and ... menu off the row or clips them, and the menu becomes unreachable. |
min-width: 0 on the text column. overflow-wrap: anywhere on .email (or ellipsis plus a title). flex-shrink: 0 on the badge and the button. |
| 3 | Ugly | avatar | Any long name or email | The <img> has width: 40 but the default flex-shrink: 1, so it can squash into a narrow oval when the text column is wide. |
flex-shrink: 0 on the avatar. Add object-fit: cover for non-square photos. |
| 4 | Ugly | avatar |
undefined, '', or a 404 URL |
There's a broken-image icon, or an empty src that may re-request the page. There's no onError and no initials fallback. |
Fall back to an initials circle on missing src or onError. |
| 5 | Ugly | name |
255 chars with spaces (such as a full legal name with several surnames) | It wraps to many lines. With align-items: center, the avatar, badge and menu float mid-height. With no margin-left: auto, the badge and menu sit right after the text, so they don't line up between rows. That last part may already show with demo data, depending on your CSS. |
align-items: flex-start. Clamp .name to 2 lines (with a title). margin-left: auto on the badge. |
| 6 | Ugly | title |
Missing, '', or ' ' |
A bare - shows under the email. Whitespace-only passes the || check and renders an empty line. |
Trim the title. Omit the line when empty. |
| 7 | Ugly | status |
Raw enum value such as pending_invite, or a localized label |
The badge shows the raw value, so underscores and lowercase appear. A long label can wrap because the badge may shrink. An unknown value or null gives a blank or meaningless pill. |
Map status to a label and a style, with a fallback. white-space: nowrap; flex-shrink: 0. |
| 8 | Ugly | count |
1, 0, 1284, null |
"1 members", "0 members", unseparated 1284, and " members" when null. |
Intl.PluralRules (or separate strings). Intl.NumberFormat. Guard null. |
| 9 | Fragile | member list | 1,000+ members | The schema has no limit, and nothing here paginates or virtualizes. If the parent maps every member, that is thousands of rows with inline style objects, with no React.memo. The Header count can also disagree with the number of rows rendered if the list is paginated. |
Virtualize or paginate (decide in Part 2). Make sure count comes from the total, not list.length. |
| 10 | Fragile | ... button |
Any | It has no accessible name (screen readers read "dot dot dot"). It has no type="button", so it submits if the row is ever inside a <form>. |
aria-label="Member actions" and type="button". |
| 11 | Fragile | <img> |
Any | There's no alt, so the screen reader may read the filename. |
alt="" if decorative, or alt={member.name}. |
| 12 | Fragile | RTL, 200% zoom, dark mode | n/a | These depend on your CSS. The inline gap and flexbox are direction-safe. Zoom will make #2 and #5 worse. Dark mode can't be judged from the JSX. |
Check these once the fixes are in. |
Part 2: Decisions for you
- Name: I recommend wrapping at 2 lines, with the full value in
title. People identify each other by name, so I wouldn't truncate it. - Email: I recommend wrapping with
overflow-wrap: anywhere. If you truncate instead, use the middle, because long emails often differ only in the local part or the domain. - Title: I recommend one line with an end ellipsis and a
title, since it's secondary info. - Missing title: I recommend omitting the line. This is a minor call, but a lone
-looks like a bug. - Long member list: I recommend pagination if the list is mostly searched or filtered. If people scroll it as a roster, virtualize it instead.
Part 3: What held up
- Escaping: React escapes all the text, so a name like
<b>x</b>or&shows literally. There's nodangerouslySetInnerHTML. - Missing title: A missing or empty
titledoesn't crash, apart from the cosmetic dash in #6. - Row layout: The row has no fixed width or height, so it can grow with its content.
- Avatar shape:
borderRadius: '50%'on a fixed 40×40 gives a circle at rest. - Demo data: With Jane Doe, the single-line layout looks fine, and "12 members" is correct.
Not covered
I haven't seen the parent list, its keys, its empty state (zero members) or its loading state. Those are worth a look next. I also didn't test emoji-first names or diacritic clipping, since both depend on CSS I can't see. Once #1 is fixed, an emoji-first name will come out wrong in any initials code that doesn't use graphemes.
The most urgent fix is #1, because it can take down the whole page. Say fix all or fix 1, 2, 3 and I'll apply them, though you asked for no file edits for now.
Test run: a small member-row component pasted as code. No browser or dev server was available, so the model said every finding is inferred and wrote no files, as asked. It did not open the Skill's CATALOG.md in this run. It found a crash for one-word names and several layout breaks.
하는 일
데모 데이터로는 멀쩡해 보이는 UI에서 '골라낸 좋은 데이터'라는 전제를 걷어 냅니다. 먼저 컴포넌트가 그리는 모든 값과 한도(검증 스키마, 데이터베이스, API)를 정리하고, 깨지기 쉬운 값의 카탈로그에서 최악의 데이터를 조립합니다. 긴 이름과 발음 기호가 있는 이름, 줄바꿈이 안 되는 이메일과 URL, 한 글자 이름과 이모지로 시작하는 이름, 정확히 1인 개수, 빈 목록과 거대한 목록, 없는 이미지와 필드 등입니다. 데이터는 데모 데이터와 같은 경로로 넣고 개발 전용 '데모 데이터 / 최악 데이터' 토글 뒤에 둡니다. 실제 컨테이너 너비, 320px, 200% 확대, 다크 모드, RTL에서 확인한 뒤 증상, 원인, 심각도(깨짐, 보기 흉함, 취약), 해결책과 함께 보고합니다. 고치기 전에 멈추므로 결정은 당신이 합니다.
결과물
발견 사항 표, 당신이 정해야 할 결정(자를지 줄바꿈할지, 빈 상태 표시), 잘 버틴 부분, 수정 적용 방법.
이런 때 좋습니다
실제 사용자보다 먼저 레이아웃과 데이터 버그를 찾고 싶은 개발자와 디자이너.
낮은 위험:지침만 있는 파일로 스크립트가 없고, 네트워크에 접속하지 않습니다. 프로젝트에서는 개발 전용 토글과 최악 데이터 파일을 추가할 수 있습니다. 먼저 보고하고 요청할 때만 고치지만 쓰여진 파일은 확인하세요. 붙여 넣은 컴포넌트로 브라우저 없이 한 번 시험했고, 모든 결과가 추론이라고 밝히며 파일을 쓰지 않았고, 한 단어 이름에서 충돌이 나는 것과 여러 레이아웃 문제를 찾았습니다. 이번에는 자체 카탈로그 파일을 열지 않았습니다. 질문 없이 호출하면 인사만 합니다. 작성자 Emil Kowalski, MIT.