QA pass: layout, create-flow, and About books #68
@@ -70,7 +70,7 @@ function ApplicableScopeFieldComponent({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`flex flex-col gap-2 ${className}`.trim()}>
|
<div className={`flex flex-col gap-2 ${className}`.trim()}>
|
||||||
<InputLabel label={label} helpIcon size="s" palette="default" />
|
<InputLabel label={label} size="s" palette="default" />
|
||||||
<div className="flex flex-wrap items-center gap-2">
|
<div className="flex flex-wrap items-center gap-2">
|
||||||
{scopes.map((scope) => {
|
{scopes.map((scope) => {
|
||||||
const isSelected = selectedScopes.includes(scope);
|
const isSelected = selectedScopes.includes(scope);
|
||||||
|
|||||||
+1
-2
@@ -60,7 +60,6 @@ function CustomMethodCardFieldBlocksSummaryViewComponent({
|
|||||||
<div key={block.id} className="flex flex-col gap-2">
|
<div key={block.id} className="flex flex-col gap-2">
|
||||||
<InputLabel
|
<InputLabel
|
||||||
label={block.blockTitle}
|
label={block.blockTitle}
|
||||||
helpIcon
|
|
||||||
size="s"
|
size="s"
|
||||||
palette="default"
|
palette="default"
|
||||||
/>
|
/>
|
||||||
@@ -123,7 +122,6 @@ function CustomMethodCardFieldBlocksSummaryViewComponent({
|
|||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<InputLabel
|
<InputLabel
|
||||||
label={block.blockTitle}
|
label={block.blockTitle}
|
||||||
helpIcon
|
|
||||||
size="s"
|
size="s"
|
||||||
palette="default"
|
palette="default"
|
||||||
/>
|
/>
|
||||||
@@ -169,6 +167,7 @@ function CustomMethodCardFieldBlocksSummaryViewComponent({
|
|||||||
<IncrementerBlock
|
<IncrementerBlock
|
||||||
key={block.id}
|
key={block.id}
|
||||||
label={block.blockTitle}
|
label={block.blockTitle}
|
||||||
|
helpIcon={false}
|
||||||
value={block.defaultPercent}
|
value={block.defaultPercent}
|
||||||
min={1}
|
min={1}
|
||||||
max={100}
|
max={100}
|
||||||
|
|||||||
+1
-1
@@ -30,7 +30,6 @@ function CustomMethodCardUploadBlockRowViewComponent({
|
|||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<InputLabel
|
<InputLabel
|
||||||
label={block.blockTitle}
|
label={block.blockTitle}
|
||||||
helpIcon
|
|
||||||
size="s"
|
size="s"
|
||||||
palette="default"
|
palette="default"
|
||||||
/>
|
/>
|
||||||
@@ -77,6 +76,7 @@ function CustomMethodCardUploadBlockRowViewComponent({
|
|||||||
) : (
|
) : (
|
||||||
<Upload
|
<Upload
|
||||||
active={!busy}
|
active={!busy}
|
||||||
|
showHelpIcon={false}
|
||||||
hintText={busy ? uploadingHint : uploadHint}
|
hintText={busy ? uploadingHint : uploadHint}
|
||||||
onClick={onUploadClick}
|
onClick={onUploadClick}
|
||||||
/>
|
/>
|
||||||
|
|||||||
+2
-5
@@ -51,12 +51,10 @@ function CustomMethodCardWizardFieldBodiesViewComponent({
|
|||||||
value={textBlockTitle}
|
value={textBlockTitle}
|
||||||
onChange={onTextBlockTitleChange}
|
onChange={onTextBlockTitleChange}
|
||||||
maxLength={CUSTOM_METHOD_CARD_WIZARD_MAX_FIELD_CHARS}
|
maxLength={CUSTOM_METHOD_CARD_WIZARD_MAX_FIELD_CHARS}
|
||||||
showHelpIcon
|
|
||||||
/>
|
/>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<InputLabel
|
<InputLabel
|
||||||
label={copy.text.placeholderLabel}
|
label={copy.text.placeholderLabel}
|
||||||
helpIcon
|
|
||||||
size="s"
|
size="s"
|
||||||
palette="default"
|
palette="default"
|
||||||
/>
|
/>
|
||||||
@@ -82,7 +80,6 @@ function CustomMethodCardWizardFieldBodiesViewComponent({
|
|||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<InputLabel
|
<InputLabel
|
||||||
label={copy.badges.blockTitleLabel}
|
label={copy.badges.blockTitleLabel}
|
||||||
helpIcon
|
|
||||||
helperText={copy.requiredHint}
|
helperText={copy.requiredHint}
|
||||||
size="s"
|
size="s"
|
||||||
palette="default"
|
palette="default"
|
||||||
@@ -127,7 +124,6 @@ function CustomMethodCardWizardFieldBodiesViewComponent({
|
|||||||
value={uploadBlockTitle}
|
value={uploadBlockTitle}
|
||||||
onChange={onUploadBlockTitleChange}
|
onChange={onUploadBlockTitleChange}
|
||||||
maxLength={CUSTOM_METHOD_CARD_WIZARD_MAX_FIELD_CHARS}
|
maxLength={CUSTOM_METHOD_CARD_WIZARD_MAX_FIELD_CHARS}
|
||||||
showHelpIcon
|
|
||||||
/>
|
/>
|
||||||
{hasUploadPreview ? (
|
{hasUploadPreview ? (
|
||||||
<div className="relative inline-block max-w-full">
|
<div className="relative inline-block max-w-full">
|
||||||
@@ -158,6 +154,7 @@ function CustomMethodCardWizardFieldBodiesViewComponent({
|
|||||||
) : (
|
) : (
|
||||||
<Upload
|
<Upload
|
||||||
active={!uploadPersisting}
|
active={!uploadPersisting}
|
||||||
|
showHelpIcon={false}
|
||||||
hintText={
|
hintText={
|
||||||
uploadPersisting && uploadBusyHint
|
uploadPersisting && uploadBusyHint
|
||||||
? uploadBusyHint
|
? uploadBusyHint
|
||||||
@@ -188,10 +185,10 @@ function CustomMethodCardWizardFieldBodiesViewComponent({
|
|||||||
value={proportionBlockTitle}
|
value={proportionBlockTitle}
|
||||||
onChange={onProportionBlockTitleChange}
|
onChange={onProportionBlockTitleChange}
|
||||||
maxLength={CUSTOM_METHOD_CARD_WIZARD_MAX_FIELD_CHARS}
|
maxLength={CUSTOM_METHOD_CARD_WIZARD_MAX_FIELD_CHARS}
|
||||||
showHelpIcon
|
|
||||||
/>
|
/>
|
||||||
<IncrementerBlock
|
<IncrementerBlock
|
||||||
label={copy.proportion.defaultLabel}
|
label={copy.proportion.defaultLabel}
|
||||||
|
helpIcon={false}
|
||||||
value={proportionDefault}
|
value={proportionDefault}
|
||||||
min={1}
|
min={1}
|
||||||
max={100}
|
max={100}
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ export default function MethodCardCustomizeModalHeader({
|
|||||||
value={titleValue}
|
value={titleValue}
|
||||||
onChange={(e) => onTitleChange(e.target.value)}
|
onChange={(e) => onTitleChange(e.target.value)}
|
||||||
inputSize="medium"
|
inputSize="medium"
|
||||||
|
showHelpIcon={false}
|
||||||
/>
|
/>
|
||||||
{showDescription ? (
|
{showDescription ? (
|
||||||
<ModalTextAreaField
|
<ModalTextAreaField
|
||||||
|
|||||||
@@ -2,8 +2,9 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Shared "labelled text area" field used by every create flow modal section.
|
* Shared "labelled text area" field used by every create flow modal section.
|
||||||
* Pairs an `InputLabel` (with help icon) with a `TextArea` set to the embedded
|
* Pairs an `InputLabel` with a `TextArea` set to the embedded appearance —
|
||||||
* appearance — matching the Figma "Control / Text Area" pattern.
|
* matching the Figma "Control / Text Area" pattern. Section help marks stay
|
||||||
|
* off until tooltip behavior ships.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { memo, useId } from "react";
|
import { memo, useId } from "react";
|
||||||
@@ -13,7 +14,7 @@ import InputLabel from "../../../components/type/InputLabel";
|
|||||||
export interface ModalTextAreaFieldProps {
|
export interface ModalTextAreaFieldProps {
|
||||||
/** Label rendered above the text area. */
|
/** Label rendered above the text area. */
|
||||||
label: string;
|
label: string;
|
||||||
/** Show the help "?" icon next to the label (default `true`). */
|
/** Show the help "?" icon next to the label. Off until tooltip behavior ships. */
|
||||||
helpIcon?: boolean;
|
helpIcon?: boolean;
|
||||||
/** Current text value. */
|
/** Current text value. */
|
||||||
value: string;
|
value: string;
|
||||||
@@ -30,7 +31,7 @@ export interface ModalTextAreaFieldProps {
|
|||||||
|
|
||||||
function ModalTextAreaFieldComponent({
|
function ModalTextAreaFieldComponent({
|
||||||
label,
|
label,
|
||||||
helpIcon = true,
|
helpIcon = false,
|
||||||
value,
|
value,
|
||||||
onChange,
|
onChange,
|
||||||
rows = 4,
|
rows = 4,
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ function DecisionApproachEditFieldsComponent({
|
|||||||
/>
|
/>
|
||||||
<IncrementerBlock
|
<IncrementerBlock
|
||||||
label={t.sectionHeadings.consensusLevel}
|
label={t.sectionHeadings.consensusLevel}
|
||||||
|
helpIcon={false}
|
||||||
value={value.consensusLevel}
|
value={value.consensusLevel}
|
||||||
min={CONSENSUS_LEVEL_MIN}
|
min={CONSENSUS_LEVEL_MIN}
|
||||||
max={CONSENSUS_LEVEL_MAX}
|
max={CONSENSUS_LEVEL_MAX}
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ export function CardStackView({
|
|||||||
recommended={item.recommended ?? false}
|
recommended={item.recommended ?? false}
|
||||||
selected={isSelected(item.id)}
|
selected={isSelected(item.id)}
|
||||||
orientation="vertical"
|
orientation="vertical"
|
||||||
showInfoIcon={true}
|
showInfoIcon={false}
|
||||||
onClick={() => onCardSelect(item.id)}
|
onClick={() => onCardSelect(item.id)}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
@@ -457,7 +457,7 @@ export function CardStackView({
|
|||||||
recommended={item.recommended ?? false}
|
recommended={item.recommended ?? false}
|
||||||
selected={isSelected(item.id)}
|
selected={isSelected(item.id)}
|
||||||
orientation="vertical"
|
orientation="vertical"
|
||||||
showInfoIcon={true}
|
showInfoIcon={false}
|
||||||
onClick={() => onCardSelect(item.id)}
|
onClick={() => onCardSelect(item.id)}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import type { SelectionProps } from "./Selection.types";
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Figma: "Card / CardSelection" — stacked tile e.g. `16775:28762` (recommended + label + supportText).
|
* Figma: "Card / CardSelection" — stacked tile e.g. `16775:28762` (recommended + label + supportText).
|
||||||
* `orientation="horizontal"` selects that vertical stack; `"vertical"` is label + optional info icon with tag on the right (CardStack `singleStack` / right-rail).
|
* `orientation="horizontal"` selects that vertical stack; `"vertical"` is label + tag on the right (CardStack `singleStack` / right-rail). Helper `?` stays off until tooltip behavior ships.
|
||||||
*/
|
*/
|
||||||
const SelectionContainer = memo<SelectionProps>(
|
const SelectionContainer = memo<SelectionProps>(
|
||||||
({
|
({
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ export interface SelectionProps {
|
|||||||
recommended?: boolean;
|
recommended?: boolean;
|
||||||
selected?: boolean;
|
selected?: boolean;
|
||||||
orientation: "horizontal" | "vertical";
|
orientation: "horizontal" | "vertical";
|
||||||
|
/** Off in product until tooltip behavior ships. */
|
||||||
showInfoIcon?: boolean;
|
showInfoIcon?: boolean;
|
||||||
/** Optional id for the root (e.g. `data-card-id` for focus after modal close). */
|
/** Optional id for the root (e.g. `data-card-id` for focus after modal close). */
|
||||||
id?: string;
|
id?: string;
|
||||||
|
|||||||
@@ -291,7 +291,7 @@ function ReadOnlyScopeField({
|
|||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<InputLabel label={label} helpIcon size="s" palette="default" />
|
<InputLabel label={label} size="s" palette="default" />
|
||||||
<div className="flex flex-wrap items-center gap-2">
|
<div className="flex flex-wrap items-center gap-2">
|
||||||
{scopes.map((scope) => (
|
{scopes.map((scope) => (
|
||||||
<Chip
|
<Chip
|
||||||
@@ -317,7 +317,7 @@ function ReadOnlyValueField({
|
|||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<InputLabel label={label} helpIcon size="s" palette="default" />
|
<InputLabel label={label} size="s" palette="default" />
|
||||||
<span className="text-medium-label text-[color:var(--color-content-default-primary)]">
|
<span className="text-medium-label text-[color:var(--color-content-default-primary)]">
|
||||||
{value}
|
{value}
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -36,7 +36,8 @@ export default {
|
|||||||
},
|
},
|
||||||
showInfoIcon: {
|
showInfoIcon: {
|
||||||
control: { type: "boolean" },
|
control: { type: "boolean" },
|
||||||
description: "Show info icon next to label (typically in vertical)",
|
description:
|
||||||
|
"Optional helper ? next to the label. Off in product until tooltip behavior ships.",
|
||||||
},
|
},
|
||||||
onClick: { action: "clicked" },
|
onClick: { action: "clicked" },
|
||||||
},
|
},
|
||||||
@@ -82,7 +83,7 @@ export const VerticalRecommended = {
|
|||||||
recommended: true,
|
recommended: true,
|
||||||
selected: false,
|
selected: false,
|
||||||
orientation: "vertical",
|
orientation: "vertical",
|
||||||
showInfoIcon: true,
|
showInfoIcon: false,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -93,7 +94,7 @@ export const VerticalSelected = {
|
|||||||
recommended: false,
|
recommended: false,
|
||||||
selected: true,
|
selected: true,
|
||||||
orientation: "vertical",
|
orientation: "vertical",
|
||||||
showInfoIcon: true,
|
showInfoIcon: false,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -134,7 +135,7 @@ export const AllVariants = {
|
|||||||
recommended={true}
|
recommended={true}
|
||||||
selected={false}
|
selected={false}
|
||||||
orientation="vertical"
|
orientation="vertical"
|
||||||
showInfoIcon={true}
|
showInfoIcon={false}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
@@ -147,7 +148,7 @@ export const AllVariants = {
|
|||||||
recommended={false}
|
recommended={false}
|
||||||
selected={true}
|
selected={true}
|
||||||
orientation="vertical"
|
orientation="vertical"
|
||||||
showInfoIcon={true}
|
showInfoIcon={false}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ export default {
|
|||||||
docs: {
|
docs: {
|
||||||
description: {
|
description: {
|
||||||
component:
|
component:
|
||||||
"Shared 'labelled text area' field used by every create-flow modal section. Pairs `InputLabel` (with help icon) with a `TextArea` set to the `embedded` appearance.",
|
"Shared 'labelled text area' field used by every create-flow modal section. Pairs `InputLabel` with a `TextArea` set to the `embedded` appearance. Section help marks stay off until tooltip behavior ships.",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -35,7 +35,7 @@ export const Default = {
|
|||||||
},
|
},
|
||||||
args: {
|
args: {
|
||||||
label: "Description",
|
label: "Description",
|
||||||
helpIcon: true,
|
helpIcon: false,
|
||||||
placeholder: "What does this rule cover?",
|
placeholder: "What does this rule cover?",
|
||||||
rows: 4,
|
rows: 4,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -119,4 +119,10 @@ describe("ApplicableScopeField behavior", () => {
|
|||||||
screen.queryByRole("textbox", { name: /Add Applicable Scope/i }),
|
screen.queryByRole("textbox", { name: /Add Applicable Scope/i }),
|
||||||
).not.toBeInTheDocument();
|
).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("does not render a section help icon", () => {
|
||||||
|
renderWithProviders(<ApplicableScopeField {...baseProps} />);
|
||||||
|
|
||||||
|
expect(screen.queryByAltText("Help")).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ describe("CommunicationMethodsScreen — Add Platform persistence", () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const dialog = await screen.findByRole("dialog");
|
const dialog = await screen.findByRole("dialog");
|
||||||
|
expect(within(dialog).queryByAltText("Help")).not.toBeInTheDocument();
|
||||||
const textboxes = within(dialog).getAllByRole("textbox");
|
const textboxes = within(dialog).getAllByRole("textbox");
|
||||||
expect(textboxes.length).toBe(3);
|
expect(textboxes.length).toBe(3);
|
||||||
const corePrincipleField = textboxes[0] as HTMLTextAreaElement;
|
const corePrincipleField = textboxes[0] as HTMLTextAreaElement;
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ describe("CustomMethodCardFieldBlocksSummary", () => {
|
|||||||
}),
|
}),
|
||||||
).toBeInTheDocument();
|
).toBeInTheDocument();
|
||||||
expect(screen.queryByRole("button", { name: "Upload" })).not.toBeInTheDocument();
|
expect(screen.queryByRole("button", { name: "Upload" })).not.toBeInTheDocument();
|
||||||
|
expect(screen.queryByAltText("Help")).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("after remove, parent can pass cleared blocks and Upload shows again", () => {
|
it("after remove, parent can pass cleared blocks and Upload shows again", () => {
|
||||||
|
|||||||
@@ -85,4 +85,29 @@ describe("ModalTextAreaField behavior", () => {
|
|||||||
|
|
||||||
expect(screen.getByRole("textbox", { name: /Notes/i })).toBeDisabled();
|
expect(screen.getByRole("textbox", { name: /Notes/i })).toBeDisabled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("does not render a section help icon by default", () => {
|
||||||
|
renderWithProviders(
|
||||||
|
<ModalTextAreaField
|
||||||
|
label="Core principle"
|
||||||
|
value=""
|
||||||
|
onChange={() => {}}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(screen.queryByAltText("Help")).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders a section help icon when helpIcon is true", () => {
|
||||||
|
renderWithProviders(
|
||||||
|
<ModalTextAreaField
|
||||||
|
label="Core principle"
|
||||||
|
helpIcon
|
||||||
|
value=""
|
||||||
|
onChange={() => {}}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(screen.getByAltText("Help")).toBeInTheDocument();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -65,6 +65,21 @@ describe("CardStack Component", () => {
|
|||||||
expect(screen.getAllByText("Option C").length).toBeGreaterThanOrEqual(1);
|
expect(screen.getAllByText("Option C").length).toBeGreaterThanOrEqual(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("hides helper icons on compact, expanded, and single-stack cards", () => {
|
||||||
|
const { unmount } = render(
|
||||||
|
<CardStack cards={SAMPLE_CARDS} expanded={false} />,
|
||||||
|
);
|
||||||
|
expect(screen.queryByText("?")).not.toBeInTheDocument();
|
||||||
|
unmount();
|
||||||
|
|
||||||
|
const expanded = render(<CardStack cards={SAMPLE_CARDS} expanded={true} />);
|
||||||
|
expect(screen.queryByText("?")).not.toBeInTheDocument();
|
||||||
|
expanded.unmount();
|
||||||
|
|
||||||
|
render(<CardStack cards={SAMPLE_CARDS} layout="singleStack" />);
|
||||||
|
expect(screen.queryByText("?")).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
test("expanded tiles use the compact 142px CardSelection height", () => {
|
test("expanded tiles use the compact 142px CardSelection height", () => {
|
||||||
render(<CardStack cards={SAMPLE_CARDS} expanded={true} />);
|
render(<CardStack cards={SAMPLE_CARDS} expanded={true} />);
|
||||||
|
|
||||||
|
|||||||
@@ -54,6 +54,24 @@ describe("Selection Component", () => {
|
|||||||
expect(card).toHaveClass("flex-row");
|
expect(card).toHaveClass("flex-row");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("does not render a helper icon by default", () => {
|
||||||
|
render(<Selection {...defaultProps} orientation="vertical" />);
|
||||||
|
|
||||||
|
expect(screen.queryByText("?")).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders a helper icon when showInfoIcon is true", () => {
|
||||||
|
render(
|
||||||
|
<Selection
|
||||||
|
{...defaultProps}
|
||||||
|
orientation="vertical"
|
||||||
|
showInfoIcon={true}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(screen.getByText("?")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
it("handles click events", () => {
|
it("handles click events", () => {
|
||||||
const handleClick = vi.fn();
|
const handleClick = vi.fn();
|
||||||
render(<Selection {...defaultProps} onClick={handleClick} />);
|
render(<Selection {...defaultProps} onClick={handleClick} />);
|
||||||
|
|||||||
Reference in New Issue
Block a user