Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
411fc6c
Add Gorgorito and Add to Avatar Primary & Secondary text
PelayoFelgueroso Oct 15, 2025
2316d9e
Add condition to render Overlay only when Gorgorito is clickable
PelayoFelgueroso Oct 15, 2025
e692e55
Fix last commit
PelayoFelgueroso Oct 15, 2025
e49e1f7
Export ref from Gorgorito
PelayoFelgueroso Oct 15, 2025
03037d0
Add person as default icon for avatar, remove default icon form Gorgo…
PelayoFelgueroso Oct 15, 2025
45fc6fd
Rename Gorgorito to ActionIcon and remove past ActionIcon
PelayoFelgueroso Oct 15, 2025
40b6021
Fix test errors
PelayoFelgueroso Oct 15, 2025
1977b3f
Remove outline when :focus-visible
PelayoFelgueroso Oct 15, 2025
33008b7
Display actionIcon as button when onClick is passed
PelayoFelgueroso Oct 15, 2025
826700a
Fix TextInput tests related to ActionIcon and DateInput
PelayoFelgueroso Oct 16, 2025
ea79015
Fix styles when rendered as button
PelayoFelgueroso Oct 16, 2025
7a1f528
Fix NumberInput test errors
PelayoFelgueroso Oct 16, 2025
ee0ccc0
Fix Select tests
PelayoFelgueroso Oct 16, 2025
523174e
Add DxcActionIcon instead of ActionIcon to Chip and Header
PelayoFelgueroso Oct 16, 2025
a51e17a
Add ransparentPrimary to color prop types of ActionIcon to display t…
PelayoFelgueroso Oct 16, 2025
8de651a
Display only transparentPrimary on DxcActionsCell when component isn'…
PelayoFelgueroso Oct 16, 2025
0f39909
Fix Dialog Test errors
PelayoFelgueroso Oct 16, 2025
ec02b0a
Fix ActionIcon unconsistencies in height
PelayoFelgueroso Oct 16, 2025
348530f
Changed outline for border when ActionIcon is focused
PelayoFelgueroso Oct 16, 2025
bdb465c
Fix outline when ActionIcon is focused
PelayoFelgueroso Oct 16, 2025
15c756d
Merge branch 'master' into PelayoFelgueroso/gorgorito
PelayoFelgueroso Oct 16, 2025
383caf6
Fix Dialog stories changes
PelayoFelgueroso Oct 16, 2025
8103fc9
Merge branch 'PelayoFelgueroso/gorgorito' of https://github.com/dxc-t…
PelayoFelgueroso Oct 16, 2025
4852366
Add comment
PelayoFelgueroso Oct 16, 2025
4b49589
Add reducedOutline prop and remove unnecesary TooltipWrapper
PelayoFelgueroso Oct 17, 2025
756a016
Fix DataGrid tests
PelayoFelgueroso Oct 17, 2025
258bf31
Changed html tags to Avatar Label & Sublabel
PelayoFelgueroso Oct 17, 2025
2329d33
Add tooltip and disabled variants to ActionIcon stories
PelayoFelgueroso Oct 20, 2025
110a797
Merge branch 'master' into PelayoFelgueroso/gorgorito
PelayoFelgueroso Oct 21, 2025
6d1acad
Fix Select tootlip condition
PelayoFelgueroso Oct 21, 2025
4b5bd96
Fix color variants, put square to be the default shape
PelayoFelgueroso Oct 21, 2025
c423fff
Normaliza outline-offset on Avatar and ActionIcon
PelayoFelgueroso Oct 21, 2025
1b4cfa2
Remove getOutlineOffset function
PelayoFelgueroso Oct 21, 2025
52ba6ec
Fix size bug when a random string is passed to the size prop
PelayoFelgueroso Oct 24, 2025
53d7656
Merge branch 'PelayoFelgueroso/gorgorito' of https://github.com/dxc-t…
PelayoFelgueroso Oct 24, 2025
6a7882d
Merge branch 'master' into PelayoFelgueroso/gorgorito
PelayoFelgueroso Oct 27, 2025
921425d
Fix Action Icon tooltip story
PelayoFelgueroso Oct 30, 2025
7efff47
Fix role assignment
PelayoFelgueroso Oct 30, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 20 additions & 12 deletions packages/lib/src/action-icon/ActionIcon.accessibility.test.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,30 @@
import { render } from "@testing-library/react";
import DxcActionIcon from "./ActionIcon";
import { axe } from "../../test/accessibility/axe-helper";
import DxcActionIcon from "./ActionIcon";

const iconSVG = (
<svg width="24px" height="24px" viewBox="0 0 24 24" fill="currentColor">
<path d="M0 0h24v24H0z" fill="none" />
<path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z" />
</svg>
);

describe("Action icon component accessibility tests", () => {
describe("ActionIcon component accessibility tests", () => {
it("Should not have basic accessibility issues", async () => {
const { container } = render(<DxcActionIcon icon={iconSVG} title="favourite" />);
const { container } = render(<DxcActionIcon icon="house" />);
const results = await axe(container);
expect(results.violations).toHaveLength(0);
});
it("Should not have basic accessibility issues when it works as a button", async () => {
const { container } = render(<DxcActionIcon icon="house" onClick={() => console.log("")} />);
const results = await axe(container);
expect(results.violations).toHaveLength(0);
});
it("Should not have basic accessibility issues when it works as an anchor", async () => {
const { container } = render(<DxcActionIcon icon="house" linkHref="/components/avatar" />);
const results = await axe(container);
expect(results.violations).toHaveLength(0);
});
it("Should not have basic accessibility issues when disabled", async () => {
const { container } = render(<DxcActionIcon icon="house" disabled />);
const results = await axe(container);
expect(results.violations).toHaveLength(0);
});
it("Should not have basic accessibility issues for disabled mode", async () => {
const { container } = render(<DxcActionIcon icon={iconSVG} title="disabled" disabled />);
it("Should not have basic accessibility issues when status is passed", async () => {
const { container } = render(<DxcActionIcon icon="house" status={{ mode: "success", position: "top" }} />);
const results = await axe(container);
expect(results.violations).toHaveLength(0);
});
Expand Down
275 changes: 209 additions & 66 deletions packages/lib/src/action-icon/ActionIcon.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,76 +1,228 @@
import { Meta, StoryObj } from "@storybook/react-vite";
import Title from "../../.storybook/components/Title";
import ExampleContainer from "../../.storybook/components/ExampleContainer";
import DxcActionIcon from "./ActionIcon";
import DxcTooltip from "../tooltip/Tooltip";
import DxcInset from "../inset/Inset";
import { userEvent, within } from "storybook/internal/test";
import DxcActionIcon from "./ActionIcon";
import DxcFlex from "../flex/Flex";
import Title from "../../.storybook/components/Title";
import ExampleContainer, { PseudoState } from "../../.storybook/components/ExampleContainer";
import { ActionIconPropTypes, Status } from "./types";

export default {
title: "Action Icon ",
title: "ActionIcon",
component: DxcActionIcon,
} satisfies Meta<typeof DxcActionIcon>;

const iconSVG = (
<svg width="24px" height="24px" viewBox="0 0 24 24" fill="currentColor">
<path d="M0 0h24v24H0z" fill="none" />
<path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z" />
</svg>
);
type Story = StoryObj<typeof DxcActionIcon>;

const ActionIcon = () => (
<>
<Title title="Default" theme="light" level={2} />
<ExampleContainer>
<DxcActionIcon icon="favorite" title="Favourite" />
</ExampleContainer>
<Title title="Disabled" theme="light" level={2} />
<ExampleContainer>
<DxcActionIcon icon="favorite" title="Favourite" disabled />
</ExampleContainer>
<Title title="Hover" theme="light" level={2} />
<ExampleContainer pseudoState="pseudo-hover">
<DxcActionIcon icon="filled_favorite" title="Favourite" />
</ExampleContainer>
<Title title="Focus" theme="light" level={2} />
<ExampleContainer pseudoState="pseudo-focus">
<DxcActionIcon icon={iconSVG} title="Favourite" />
</ExampleContainer>
<Title title="Active" theme="light" level={2} />
<ExampleContainer pseudoState="pseudo-active">
<DxcActionIcon icon={iconSVG} title="Favourite" />
</ExampleContainer>
</>
);
type GroupingKey = "size" | "shape" | "color" | "statusPosition" | "statusMode" | "pseudoState";

const Tooltip = () => (
<>
<Title title="Default tooltip" theme="light" level={2} />
<ExampleContainer>
<DxcActionIcon icon="favorite" title="Favourite" />
</ExampleContainer>
</>
);
type ActionIconRowProps = {
sizes?: ActionIconPropTypes["size"][];
shapes?: ActionIconPropTypes["shape"][];
colors?: ActionIconPropTypes["color"][];
icon?: ActionIconPropTypes["icon"];
statusModes?: Status["mode"][];
statusPositions?: (Status["position"] | undefined)[];
pseudoStates?: (PseudoState | "disabled" | undefined)[];
groupBy?: GroupingKey[];
};

const ActionIconRow = ({
sizes = ["medium"],
shapes = ["circle"],
colors = ["neutral"],
statusModes,
statusPositions = [],
pseudoStates = [],
groupBy = ["size"],
}: ActionIconRowProps) => {
const getValuesForKey = (key?: GroupingKey) => {
switch (key) {
case "size":
return sizes as string[];
case "shape":
return shapes as string[];
case "color":
return colors as string[];
case "statusPosition":
return statusPositions as string[];
case "statusMode":
return statusModes as string[];
case "pseudoState":
return pseudoStates;
default:
return [];
}
};

const renderGroup = (
level: number,
filters: {
size?: ActionIconPropTypes["size"];
shape?: ActionIconPropTypes["shape"];
color?: ActionIconPropTypes["color"];
statusMode?: Status["mode"];
statusPosition?: Status["position"];
pseudoState?: PseudoState | "disabled";
}
): JSX.Element | JSX.Element[] => {
if (level >= groupBy.length) {
const sizesToRender = filters.size ? [filters.size] : sizes;
const colorsToRender = filters.color ? [filters.color] : colors;
const shapesToRender = filters.shape ? [filters.shape] : shapes;
const positionsToRender = filters.statusPosition
? [filters.statusPosition]
: statusPositions.length
? statusPositions
: [undefined];
const modesToRender = filters.statusMode ? [filters.statusMode] : statusModes?.length ? statusModes : [undefined];

const pseudoStatesEnabled = !!filters.pseudoState;
const isDisabled = filters.pseudoState === "disabled";
const validPseudoState = isDisabled ? undefined : (filters.pseudoState as PseudoState | undefined);

return shapesToRender.map((shape) => (
<DxcFlex
key={`shape-${shape}-${String(filters.size ?? "all")}-${String(filters.color ?? "all")}`}
gap="var(--spacing-gap-l)"
wrap="wrap"
>
{sizesToRender.map((size) =>
colorsToRender.map((color) =>
positionsToRender.map((position) =>
modesToRender.map((mode) => (
<ExampleContainer
key={`${size}-${shape}-${color}-${mode}-${position ?? "none"}`}
pseudoState={validPseudoState}
>
<DxcActionIcon
icon="settings"
size={size}
shape={shape}
color={color}
status={position && mode ? { position, mode: mode } : undefined}
onClick={pseudoStatesEnabled ? () => console.log("") : undefined}
disabled={isDisabled}
/>
</ExampleContainer>
))
)
)
)}
</DxcFlex>
));
}

const NestedTooltip = () => (
const key = groupBy[level];
const values = getValuesForKey(key);

return values.map((value) => {
const newFilters = { ...filters };
if (key === "size") newFilters.size = value as ActionIconPropTypes["size"];
else if (key === "shape") newFilters.shape = value as ActionIconPropTypes["shape"];
else if (key === "color") newFilters.color = value as ActionIconPropTypes["color"];
else if (key === "statusPosition") newFilters.statusPosition = value as Status["position"];
else if (key === "statusMode") newFilters.statusMode = value as Status["mode"];
else if (key === "pseudoState") newFilters.pseudoState = value as PseudoState | "disabled";

return (
<div key={`${key}-${String(value)}`}>
<Title title={String(value)} theme="light" level={3 + level} />
{renderGroup(level + 1, newFilters)}
</div>
);
});
};

return <>{renderGroup(0, {})}</>;
};

export const Shapes: Story = {
render: () => (
<>
<Title title="Shapes" theme="light" level={2} />
<ActionIconRow
sizes={["xsmall", "small", "medium", "large", "xlarge", "xxlarge"]}
shapes={["circle", "square"]}
groupBy={["shape", "size"]}
/>
</>
),
};

export const Colors: Story = {
render: () => (
<>
<Title title="Colors" theme="light" level={2} />
<ActionIconRow
sizes={["medium"]}
shapes={["circle"]}
colors={["neutral", "primary", "secondary", "tertiary", "success", "warning", "error", "info", "transparent"]}
groupBy={["color"]}
/>
</>
),
};

export const Statuses: Story = {
render: () => (
<>
<Title title="Statuses" theme="light" level={2} />
<ActionIconRow
sizes={["xsmall", "small", "medium", "large", "xlarge", "xxlarge"]}
colors={["neutral", "primary", "secondary", "tertiary", "success", "warning", "error", "info", "transparent"]}
shapes={["circle"]}
statusModes={["default", "info", "success", "warning", "error"]}
statusPositions={["top", "bottom"]}
groupBy={["statusPosition", "statusMode", "color"]}
/>
</>
),
};

export const PseudoStates: Story = {
render: () => (
<>
<Title title="Pseudo states" theme="light" level={2} />
<ActionIconRow
sizes={["xsmall", "small", "medium", "large", "xlarge", "xxlarge"]}
shapes={["circle"]}
statusModes={["success"]}
statusPositions={[undefined, "top", "bottom"]}
pseudoStates={[undefined, "pseudo-hover", "pseudo-focus", "pseudo-active", "disabled"]}
groupBy={["pseudoState", "size"]}
/>
</>
),
};

export const Types: Story = {
render: () => (
<>
<Title title="Icon (custom)" theme="light" level={2} />
<ActionIconRow
sizes={["xsmall", "small", "medium", "large", "xlarge", "xxlarge"]}
shapes={["circle"]}
groupBy={["size"]}
/>
<Title title="Icon (default)" theme="light" level={2} />
<ActionIconRow
sizes={["xsmall", "small", "medium", "large", "xlarge", "xxlarge"]}
shapes={["circle"]}
groupBy={["size"]}
/>
</>
),
};

const Tooltip = () => (
<>
<Title title="Nested tooltip" theme="light" level={2} />
<Title title="Default tooltip" theme="ligth" level={2} />
<ExampleContainer>
<DxcInset top="var(--spacing-padding-xxl)">
<DxcTooltip label="Favourite" position="top">
<DxcActionIcon icon="favorite" title="Favourite" />
</DxcTooltip>
</DxcInset>
<DxcActionIcon title="Home" icon="home" color="neutral" onClick={() => console.log()} />
</ExampleContainer>
</>
);

type Story = StoryObj<typeof DxcActionIcon>;

export const Chromatic: Story = {
render: ActionIcon,
};

export const ActionIconTooltip: Story = {
render: Tooltip,
play: async ({ canvasElement }) => {
Expand All @@ -79,12 +231,3 @@ export const ActionIconTooltip: Story = {
await userEvent.hover(button);
},
};

export const NestedActionIconTooltip: Story = {
render: NestedTooltip,
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const button = await canvas.findByRole("button");
await userEvent.hover(button);
},
};
Loading