Full cleanup pass

This commit is contained in:
adilallo
2026-05-21 23:25:56 -06:00
parent 28de8ef3bc
commit 99f535f821
149 changed files with 2623 additions and 1242 deletions
@@ -1,6 +1,7 @@
"use client";
import { memo, useState, useEffect, useRef } from "react";
import { useTranslation } from "../../../contexts/MessagesContext";
import ChipView from "./Chip.view";
import type { ChipProps } from "./Chip.types";
@@ -22,6 +23,7 @@ const ChipContainer = memo<ChipProps>(
onClose,
ariaLabel,
}) => {
const t = useTranslation("controlsChrome");
const state = stateProp;
const palette = paletteProp;
const size = sizeProp;
@@ -92,6 +94,9 @@ const ChipContainer = memo<ChipProps>(
onInputKeyDown={isCustom ? handleKeyDown : undefined}
inputRef={isCustom ? inputRef : undefined}
ariaLabel={ariaLabel}
confirmAriaLabel={t("chipConfirm")}
typeToAddPlaceholder={t("chipTypeToAdd")}
closeAriaLabel={t("chipClose")}
/>
);
},
@@ -68,4 +68,7 @@ export interface ChipViewProps {
onInputKeyDown?: (event: React.KeyboardEvent<HTMLInputElement>) => void;
inputRef?: React.RefObject<HTMLInputElement>;
ariaLabel?: string;
confirmAriaLabel: string;
typeToAddPlaceholder: string;
closeAriaLabel: string;
}
+6 -3
View File
@@ -19,6 +19,9 @@ function ChipView({
onInputKeyDown,
inputRef,
ariaLabel,
confirmAriaLabel,
typeToAddPlaceholder,
closeAriaLabel,
}: ChipViewProps) {
// The container is the source of truth for `disabled`. This allows
// `state="disabled"` to be used purely as a visual (for toggle-group chips
@@ -167,7 +170,7 @@ function ChipView({
<button
type="button"
className="flex items-center justify-center p-[var(--measures-spacing-150,6px)] rounded-full hover:bg-[var(--color-surface-default-semi-opaque,rgba(0,0,0,0.1))] transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
aria-label="Confirm"
aria-label={confirmAriaLabel}
disabled={!inputValue || !inputValue.trim()}
onClick={(event) => {
event.stopPropagation();
@@ -204,7 +207,7 @@ function ChipView({
value={inputValue ?? ""}
onChange={(e) => onInputChange?.(e.target.value)}
onKeyDown={onInputKeyDown}
placeholder="Type to add"
placeholder={typeToAddPlaceholder}
className="bg-transparent border-none outline-none flex-1 min-w-0 font-inter font-normal text-[color:var(--color-content-default-tertiary,#b4b4b4)] placeholder:text-[color:var(--color-content-default-tertiary,#b4b4b4)]"
style={{
fontSize: isSmall
@@ -222,7 +225,7 @@ function ChipView({
<button
type="button"
className="flex items-center justify-center p-[var(--measures-spacing-150,6px)] rounded-full hover:bg-[var(--color-surface-default-semi-opaque,rgba(0,0,0,0.1))] transition-colors"
aria-label="Close"
aria-label={closeAriaLabel}
onClick={(event) => {
event.stopPropagation();
onClose(event);
@@ -1,6 +1,7 @@
"use client";
import { memo } from "react";
import { useTranslation } from "../../../contexts/MessagesContext";
import MultiSelectView from "./MultiSelect.view";
import type { MultiSelectProps } from "./MultiSelect.types";
@@ -18,12 +19,13 @@ const MultiSelectContainer = memo<MultiSelectProps>(
onChipClick,
onAddClick,
addButton: addButtonProp = true,
addButtonText = "Add organization type",
addButtonText,
formHeader = true,
onCustomChipConfirm,
onCustomChipClose,
className = "",
}) => {
const t = useTranslation("controlsChrome");
const size = sizeProp;
const palette = paletteProp;
@@ -38,6 +40,9 @@ const MultiSelectContainer = memo<MultiSelectProps>(
onAddClick={onAddClick}
addButton={addButtonProp}
addButtonText={addButtonText}
addButtonAriaLabel={
addButtonText || t("multiSelectAddFallback")
}
formHeader={formHeader}
onCustomChipConfirm={onCustomChipConfirm}
onCustomChipClose={onCustomChipClose}
@@ -74,7 +74,8 @@ export interface MultiSelectViewProps {
onChipClick?: (chipId: string) => void;
onAddClick?: () => void;
addButton: boolean;
addButtonText: string;
addButtonText?: string;
addButtonAriaLabel: string;
formHeader: boolean;
onCustomChipConfirm?: (chipId: string, value: string) => void;
onCustomChipClose?: (chipId: string) => void;
@@ -15,6 +15,7 @@ function MultiSelectView({
onAddClick,
addButton,
addButtonText,
addButtonAriaLabel,
formHeader = true,
onCustomChipConfirm,
onCustomChipClose,
@@ -81,7 +82,7 @@ function MultiSelectView({
{addButton && (
<button
type="button"
aria-label={addButtonText || "Add option"}
aria-label={addButtonAriaLabel}
onClick={(e) => {
e.stopPropagation();
onAddClick?.();
@@ -5,10 +5,11 @@ import { forwardRef, memo } from "react";
interface SelectDropdownProps extends React.HTMLAttributes<HTMLDivElement> {
className?: string;
children?: React.ReactNode;
ariaLabel: string;
}
const SelectDropdown = forwardRef<HTMLDivElement, SelectDropdownProps>(
({ className = "", children, ...props }, ref) => {
({ className = "", children, ariaLabel, ...props }, ref) => {
const menuClasses = `
bg-black
border border-[var(--color-border-default-tertiary)]
@@ -27,7 +28,7 @@ const SelectDropdown = forwardRef<HTMLDivElement, SelectDropdownProps>(
ref={ref}
className={menuClasses}
role="listbox"
aria-label="Select an option"
aria-label={ariaLabel}
style={{ backgroundColor: "#000000" }}
{...props}
>
@@ -14,6 +14,7 @@ import React, {
useEffect,
} from "react";
import { useClickOutside } from "../../../hooks";
import { useTranslation } from "../../../contexts/MessagesContext";
import { SelectInputView } from "./SelectInput.view";
import type { SelectInputProps } from "./SelectInput.types";
@@ -38,7 +39,7 @@ const SelectInputContainer = forwardRef<HTMLButtonElement, SelectInputProps>(
textHint = false,
disabled = false,
error = false,
placeholder = "Choose an option",
placeholder,
className = "",
children,
value,
@@ -48,6 +49,9 @@ const SelectInputContainer = forwardRef<HTMLButtonElement, SelectInputProps>(
},
ref,
) => {
const t = useTranslation("controlsChrome");
const resolvedPlaceholder = placeholder ?? t("selectPlaceholder");
// Determine if label should be shown
const shouldShowLabel =
showLabel !== undefined ? showLabel : labelText !== undefined;
@@ -181,13 +185,13 @@ const SelectInputContainer = forwardRef<HTMLButtonElement, SelectInputProps>(
// Get display text for selected value
const getDisplayText = (): string => {
if (!selectedValue) return placeholder;
if (!selectedValue) return resolvedPlaceholder;
if (options && Array.isArray(options)) {
const selectedOption = options.find(
(option) => option.value === selectedValue,
);
return selectedOption ? selectedOption.label : placeholder;
return selectedOption ? selectedOption.label : resolvedPlaceholder;
}
const selectedOption = Children.toArray(children).find(
@@ -207,13 +211,13 @@ const SelectInputContainer = forwardRef<HTMLButtonElement, SelectInputProps>(
);
return selectedOption
? String(selectedOption.props.children)
: placeholder;
: resolvedPlaceholder;
};
return (
<SelectInputView
label={shouldShowLabel ? labelText : undefined}
placeholder={placeholder}
placeholder={resolvedPlaceholder}
state={actualState}
disabled={disabled}
error={error}
@@ -241,6 +245,8 @@ const SelectInputContainer = forwardRef<HTMLButtonElement, SelectInputProps>(
textData={textData}
iconRight={iconRight}
textHint={textHint}
selectAriaLabel={t("selectAriaLabel")}
hintDefault={t("hintDefault")}
{...props}
/>
);
@@ -40,6 +40,8 @@ export interface SelectInputViewProps {
textData?: boolean;
iconRight?: boolean;
textHint?: boolean;
selectAriaLabel: string;
hintDefault: string;
}
export function SelectInputView({
@@ -72,6 +74,8 @@ export function SelectInputView({
textData = true,
iconRight = true,
textHint = false,
selectAriaLabel,
hintDefault,
}: SelectInputViewProps) {
// Styles based on Figma design
const containerClasses = "flex flex-col gap-[8px]";
@@ -222,7 +226,7 @@ export function SelectInputView({
ref={menuRef}
className="absolute top-full left-0 right-0 z-50 mt-1"
>
<SelectDropdown>
<SelectDropdown ariaLabel={selectAriaLabel}>
{options && Array.isArray(options)
? options.map((option) => (
<SelectOption
@@ -268,7 +272,7 @@ export function SelectInputView({
{textHint && (
<div className="flex items-start relative shrink-0 w-full">
<p className="flex-[1_0_0] font-inter font-normal leading-[16px] min-h-px min-w-px relative text-[color:var(--color-content-default-tertiary,#b4b4b4)] text-[length:var(--sizing-300,12px)]">
Hint text here
{hintDefault}
</p>
</div>
)}
@@ -1,6 +1,7 @@
"use client";
import { memo, useCallback, useId, forwardRef } from "react";
import { useTranslation } from "../../../contexts/MessagesContext";
import { SwitchView } from "./Switch.view";
import type { SwitchProps } from "./Switch.types";
@@ -10,6 +11,7 @@ import type { SwitchProps } from "./Switch.types";
*/
const SwitchContainer = memo(
forwardRef<HTMLButtonElement, SwitchProps>((props, ref) => {
const t = useTranslation("controlsChrome");
const {
propSwitch = false,
onChange,
@@ -154,6 +156,7 @@ const SwitchContainer = memo(
trackClasses={trackClasses}
thumbClasses={thumbClasses}
labelClasses={labelClasses}
switchAriaLabel={text ?? t("toggleSwitch")}
onClick={handleClick}
onKeyDown={handleKeyDown}
onFocus={handleFocus}
@@ -37,6 +37,7 @@ export interface SwitchViewProps {
trackClasses: string;
thumbClasses: string;
labelClasses: string;
switchAriaLabel: string;
onClick: (_e: React.MouseEvent<HTMLButtonElement>) => void;
onKeyDown: (_e: React.KeyboardEvent<HTMLButtonElement>) => void;
onFocus: (_e: React.FocusEvent<HTMLButtonElement>) => void;
@@ -11,6 +11,7 @@ export const SwitchView = forwardRef<HTMLButtonElement, SwitchViewProps>(
trackClasses,
thumbClasses,
labelClasses,
switchAriaLabel,
onClick,
onKeyDown,
onFocus,
@@ -27,7 +28,7 @@ export const SwitchView = forwardRef<HTMLButtonElement, SwitchViewProps>(
type="button"
role="switch"
aria-checked={propSwitch}
aria-label={text || "Toggle switch"}
aria-label={switchAriaLabel}
onClick={onClick}
onKeyDown={onKeyDown}
onFocus={onFocus}
@@ -2,6 +2,7 @@
import { memo, forwardRef } from "react";
import { useComponentId, useFormField } from "../../../hooks";
import { useTranslation } from "../../../contexts/MessagesContext";
import { TextAreaView } from "./TextArea.view";
import type { TextAreaProps } from "./TextArea.types";
@@ -35,6 +36,7 @@ const TextAreaContainer = forwardRef<HTMLTextAreaElement, TextAreaProps>(
},
ref,
) => {
const t = useTranslation("controlsChrome");
const size = sizeProp;
const labelVariant = labelVariantProp;
const state = stateProp;
@@ -200,6 +202,8 @@ const TextAreaContainer = forwardRef<HTMLTextAreaElement, TextAreaProps>(
formHeader={formHeader}
showHelpIcon={showHelpIcon}
appearance={appearance}
helpIconAlt={t("helpIconAlt")}
hintDefault={t("hintDefault")}
{...props}
/>
);
@@ -79,4 +79,6 @@ export interface TextAreaViewProps {
formHeader?: boolean;
showHelpIcon?: boolean;
appearance?: "default" | "embedded";
helpIconAlt: string;
hintDefault: string;
}
@@ -25,6 +25,8 @@ export const TextAreaView = forwardRef<HTMLTextAreaElement, TextAreaViewProps>(
formHeader = true,
showHelpIcon = false,
appearance: _appearance,
helpIconAlt,
hintDefault,
// Component-only props: do not pass to DOM
size: _size,
labelVariant: _labelVariant,
@@ -51,7 +53,7 @@ export const TextAreaView = forwardRef<HTMLTextAreaElement, TextAreaViewProps>(
{/* eslint-disable-next-line @next/next/no-img-element -- icon asset */}
<img
src={getAssetPath(ASSETS.ICON_HELP)}
alt="Help"
alt={helpIconAlt}
className="block max-w-none size-full"
/>
</div>
@@ -81,7 +83,7 @@ export const TextAreaView = forwardRef<HTMLTextAreaElement, TextAreaViewProps>(
{textHint ? (
<div className="flex items-start relative shrink-0 w-full">
<p className="flex-[1_0_0] font-inter font-normal leading-[16px] min-h-px min-w-px relative text-[color:var(--color-content-default-tertiary,#b4b4b4)] text-[length:var(--sizing-300,12px)]">
{typeof textHint === "string" ? textHint : "Hint text here"}
{typeof textHint === "string" ? textHint : hintDefault}
</p>
</div>
) : null}
@@ -2,6 +2,7 @@
import { memo, forwardRef, useState, useRef } from "react";
import { useComponentId, useFormField } from "../../../hooks";
import { useTranslation } from "../../../contexts/MessagesContext";
import { TextInputView } from "./TextInput.view";
import type { TextInputProps } from "./TextInput.types";
@@ -34,6 +35,7 @@ const TextInputContainer = forwardRef<HTMLInputElement, TextInputProps>(
},
ref,
) => {
const t = useTranslation("controlsChrome");
const externalState = externalStateProp;
const inputSize = inputSizeProp;
@@ -244,6 +246,8 @@ const TextInputContainer = forwardRef<HTMLInputElement, TextInputProps>(
textHint={textHint}
formHeader={formHeader}
maxLength={maxLength}
helpIconAlt={t("helpIconAlt")}
hintDefault={t("hintDefault")}
{...props}
/>
);
@@ -65,4 +65,6 @@ export interface TextInputViewProps {
textHint?: boolean | string;
formHeader?: boolean;
maxLength?: number;
helpIconAlt: string;
hintDefault: string;
}
@@ -29,6 +29,8 @@ export const TextInputView = forwardRef<HTMLInputElement, TextInputViewProps>(
textHint = false,
formHeader = true,
maxLength,
helpIconAlt,
hintDefault,
},
ref,
) => {
@@ -49,7 +51,7 @@ export const TextInputView = forwardRef<HTMLInputElement, TextInputViewProps>(
{/* eslint-disable-next-line @next/next/no-img-element -- icon asset */}
<img
src={getAssetPath(ASSETS.ICON_HELP)}
alt="Help"
alt={helpIconAlt}
className="block max-w-none size-full"
/>
</div>
@@ -83,7 +85,7 @@ export const TextInputView = forwardRef<HTMLInputElement, TextInputViewProps>(
{textHint && (
<div className="flex items-start relative shrink-0 w-full">
<p className="flex-[1_0_0] font-inter font-normal leading-[16px] min-h-px min-w-px relative text-[color:var(--color-content-default-tertiary,#b4b4b4)] text-[length:var(--sizing-300,12px)]">
{typeof textHint === "string" ? textHint : "Hint text here"}
{typeof textHint === "string" ? textHint : hintDefault}
</p>
</div>
)}
@@ -1,6 +1,7 @@
"use client";
import { memo, useCallback, useId, forwardRef } from "react";
import { useTranslation } from "../../../contexts/MessagesContext";
import { ToggleGroupView } from "./ToggleGroup.view";
import type { ToggleGroupProps } from "./ToggleGroup.types";
@@ -10,6 +11,7 @@ import type { ToggleGroupProps } from "./ToggleGroup.types";
*/
const ToggleGroupContainer = memo(
forwardRef<HTMLButtonElement, ToggleGroupProps>((props, _ref) => {
const t = useTranslation("controlsChrome");
const {
children,
className = "",
@@ -131,6 +133,7 @@ const ToggleGroupContainer = memo(
state={state}
showText={showText}
ariaLabel={ariaLabel}
defaultToggleOptionAriaLabel={t("toggleOption")}
toggleClasses={toggleClasses}
onClick={handleClick}
onKeyDown={handleKeyDown}
@@ -35,6 +35,7 @@ export interface ToggleGroupViewProps {
state: "default" | "hover" | "focus" | "selected";
showText: boolean;
ariaLabel?: string;
defaultToggleOptionAriaLabel: string;
toggleClasses: string;
onClick: (_e: React.MouseEvent<HTMLButtonElement>) => void;
onKeyDown: (_e: React.KeyboardEvent<HTMLButtonElement>) => void;
@@ -8,6 +8,7 @@ export function ToggleGroupView({
state: _state,
showText,
ariaLabel,
defaultToggleOptionAriaLabel,
toggleClasses,
onClick,
onKeyDown,
@@ -20,7 +21,7 @@ export function ToggleGroupView({
id={groupId}
type="button"
role="button"
aria-label={ariaLabel || (showText ? undefined : "Toggle option")}
aria-label={ariaLabel || (showText ? undefined : defaultToggleOptionAriaLabel)}
onClick={onClick}
onKeyDown={onKeyDown}
onFocus={onFocus}
@@ -1,6 +1,7 @@
"use client";
import { memo } from "react";
import { useTranslation } from "../../../contexts/MessagesContext";
import UploadView from "./Upload.view";
import type { UploadProps } from "./Upload.types";
@@ -13,16 +14,20 @@ const UploadContainer = memo<UploadProps>(
active = true,
label,
showHelpIcon = true,
hintText = "Add image from your device",
hintText,
onClick,
className = "",
}) => {
const t = useTranslation("controlsChrome");
return (
<UploadView
active={active}
label={label}
showHelpIcon={showHelpIcon}
hintText={hintText}
hintText={hintText ?? t("uploadHintDefault")}
uploadButtonLabel={t("uploadButton")}
uploadAriaLabel={t("uploadAriaLabel")}
onClick={onClick}
className={className}
/>
@@ -35,6 +35,8 @@ export interface UploadViewProps {
label?: string;
showHelpIcon: boolean;
hintText: string;
uploadButtonLabel: string;
uploadAriaLabel: string;
onClick?: () => void;
className: string;
}
@@ -9,6 +9,8 @@ function UploadView({
label,
showHelpIcon = true,
hintText,
uploadButtonLabel,
uploadAriaLabel,
onClick,
className = "",
}: UploadViewProps) {
@@ -56,7 +58,7 @@ function UploadView({
type="button"
onClick={onClick}
className={`${buttonBgClass} flex gap-[var(--measures-spacing-150,6px)] items-center justify-center overflow-clip px-[var(--space-400,16px)] py-[var(--measures-spacing-300,12px)] rounded-[var(--measures-radius-full,9999px)] shrink-0 hover:opacity-80 transition-opacity`}
aria-label="Upload"
aria-label={uploadAriaLabel}
>
{/* Upload icon */}
<div className={`relative shrink-0 size-[20px] ${iconColor}`}>
@@ -98,7 +100,7 @@ function UploadView({
<div
className={`flex flex-col font-inter font-medium justify-center leading-[0] relative shrink-0 text-[length:var(--sizing-400,16px)] whitespace-nowrap ${buttonTextColor}`}
>
<p className="leading-[20px]">Upload</p>
<p className="leading-[20px]">{uploadButtonLabel}</p>
</div>
</button>