import React, { ReactNode, useEffect, useId, useRef } from 'react'; import { Box, Text } from 'folds'; import { BreakWord } from '../../styles/Text.css'; type SettingTileProps = { title?: ReactNode; description?: ReactNode; before?: ReactNode; after?: ReactNode; children?: ReactNode; }; export function SettingTile({ title, description, before, after, children }: SettingTileProps) { const titleId = useId(); const afterRef = useRef(null); // [Gitea #185] The control in `after` is almost always an icon-only Switch // (or a bare select/input) whose only visible label is this tile's title. // Point it at the title so screen readers announce "Show Notifications, // switch, on" instead of "switch, on". Explicit labels are left alone. useEffect(() => { if (!title || !afterRef.current) return; afterRef.current .querySelectorAll('[role="switch"], input, select, textarea, [role="combobox"]') .forEach((el) => { if (el.hasAttribute('aria-label') || el.hasAttribute('aria-labelledby')) return; if ( el.tagName === 'INPUT' && el.id && afterRef.current?.querySelector(`label[for="${el.id}"]`) ) return; el.setAttribute('aria-labelledby', titleId); }); }); return ( {before && {before}} {title && ( {title} )} {description && ( {description} )} {children} {after && ( {after} )} ); }