Skip to content

Commit 64b072c

Browse files
Merge branch 'master' into PelayoFelgueroso/quickNav-testing
2 parents 3cd5f6f + d5a4f35 commit 64b072c

File tree

31 files changed

+1117
-713
lines changed

31 files changed

+1117
-713
lines changed

apps/website/screens/components/avatar/code/AvatarCodePage.tsx

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,22 @@ const sections = [
9292
<td>This function will be called when the user clicks the avatar. Makes it behave as a button.</td>
9393
<td>-</td>
9494
</tr>
95+
<tr>
96+
<td>primaryText</td>
97+
<td>
98+
<TableCode>string</TableCode>
99+
</td>
100+
<td>Text to be displayed as label next to the avatar.</td>
101+
<td>-</td>
102+
</tr>
103+
<tr>
104+
<td>secondaryText</td>
105+
<td>
106+
<TableCode>string</TableCode>
107+
</td>
108+
<td>Text to be displayed as sublabel next to the avatar.</td>
109+
<td>-</td>
110+
</tr>
95111
<tr>
96112
<td>shape</td>
97113
<td>
@@ -128,7 +144,9 @@ const sections = [
128144
<td>
129145
<TableCode>number</TableCode>
130146
</td>
131-
<td>Value of the tabindex attribute. It will only apply when the onClick property is passed.</td>
147+
<td>
148+
Value of the tabindex attribute. It will only apply when the onClick or linkHref property is passed.
149+
</td>
132150
<td>
133151
<TableCode>0</TableCode>
134152
</td>

apps/website/screens/components/avatar/code/examples/basicUsage.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@ const code = `() => {
44
return (
55
<DxcInset space="var(--spacing-padding-xl)">
66
<DxcAvatar
7-
label="John Doe"
7+
label="Michael Ramirez"
88
color="success"
9+
primaryText="Michael Ramirez"
10+
secondaryText="m.ramirez@insurance.com"
911
/>
1012
</DxcInset>
1113
);

packages/lib/src/action-icon/ActionIcon.accessibility.test.tsx

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,30 @@
11
import { render } from "@testing-library/react";
2-
import DxcActionIcon from "./ActionIcon";
32
import { axe } from "../../test/accessibility/axe-helper";
3+
import DxcActionIcon from "./ActionIcon";
44

5-
const iconSVG = (
6-
<svg width="24px" height="24px" viewBox="0 0 24 24" fill="currentColor">
7-
<path d="M0 0h24v24H0z" fill="none" />
8-
<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" />
9-
</svg>
10-
);
11-
12-
describe("Action icon component accessibility tests", () => {
5+
describe("ActionIcon component accessibility tests", () => {
136
it("Should not have basic accessibility issues", async () => {
14-
const { container } = render(<DxcActionIcon icon={iconSVG} title="favourite" />);
7+
const { container } = render(<DxcActionIcon icon="house" />);
8+
const results = await axe(container);
9+
expect(results.violations).toHaveLength(0);
10+
});
11+
it("Should not have basic accessibility issues when it works as a button", async () => {
12+
const { container } = render(<DxcActionIcon icon="house" onClick={() => console.log("")} />);
13+
const results = await axe(container);
14+
expect(results.violations).toHaveLength(0);
15+
});
16+
it("Should not have basic accessibility issues when it works as an anchor", async () => {
17+
const { container } = render(<DxcActionIcon icon="house" linkHref="/components/avatar" />);
18+
const results = await axe(container);
19+
expect(results.violations).toHaveLength(0);
20+
});
21+
it("Should not have basic accessibility issues when disabled", async () => {
22+
const { container } = render(<DxcActionIcon icon="house" disabled />);
1523
const results = await axe(container);
1624
expect(results.violations).toHaveLength(0);
1725
});
18-
it("Should not have basic accessibility issues for disabled mode", async () => {
19-
const { container } = render(<DxcActionIcon icon={iconSVG} title="disabled" disabled />);
26+
it("Should not have basic accessibility issues when status is passed", async () => {
27+
const { container } = render(<DxcActionIcon icon="house" status={{ mode: "success", position: "top" }} />);
2028
const results = await axe(container);
2129
expect(results.violations).toHaveLength(0);
2230
});

packages/lib/src/action-icon/ActionIcon.stories.tsx

Lines changed: 209 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,76 +1,228 @@
11
import { Meta, StoryObj } from "@storybook/react-vite";
2-
import Title from "../../.storybook/components/Title";
3-
import ExampleContainer from "../../.storybook/components/ExampleContainer";
4-
import DxcActionIcon from "./ActionIcon";
5-
import DxcTooltip from "../tooltip/Tooltip";
6-
import DxcInset from "../inset/Inset";
72
import { userEvent, within } from "storybook/internal/test";
3+
import DxcActionIcon from "./ActionIcon";
4+
import DxcFlex from "../flex/Flex";
5+
import Title from "../../.storybook/components/Title";
6+
import ExampleContainer, { PseudoState } from "../../.storybook/components/ExampleContainer";
7+
import { ActionIconPropTypes, Status } from "./types";
88

99
export default {
10-
title: "Action Icon ",
10+
title: "ActionIcon",
1111
component: DxcActionIcon,
1212
} satisfies Meta<typeof DxcActionIcon>;
1313

14-
const iconSVG = (
15-
<svg width="24px" height="24px" viewBox="0 0 24 24" fill="currentColor">
16-
<path d="M0 0h24v24H0z" fill="none" />
17-
<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" />
18-
</svg>
19-
);
14+
type Story = StoryObj<typeof DxcActionIcon>;
2015

21-
const ActionIcon = () => (
22-
<>
23-
<Title title="Default" theme="light" level={2} />
24-
<ExampleContainer>
25-
<DxcActionIcon icon="favorite" title="Favourite" />
26-
</ExampleContainer>
27-
<Title title="Disabled" theme="light" level={2} />
28-
<ExampleContainer>
29-
<DxcActionIcon icon="favorite" title="Favourite" disabled />
30-
</ExampleContainer>
31-
<Title title="Hover" theme="light" level={2} />
32-
<ExampleContainer pseudoState="pseudo-hover">
33-
<DxcActionIcon icon="filled_favorite" title="Favourite" />
34-
</ExampleContainer>
35-
<Title title="Focus" theme="light" level={2} />
36-
<ExampleContainer pseudoState="pseudo-focus">
37-
<DxcActionIcon icon={iconSVG} title="Favourite" />
38-
</ExampleContainer>
39-
<Title title="Active" theme="light" level={2} />
40-
<ExampleContainer pseudoState="pseudo-active">
41-
<DxcActionIcon icon={iconSVG} title="Favourite" />
42-
</ExampleContainer>
43-
</>
44-
);
16+
type GroupingKey = "size" | "shape" | "color" | "statusPosition" | "statusMode" | "pseudoState";
4517

46-
const Tooltip = () => (
47-
<>
48-
<Title title="Default tooltip" theme="light" level={2} />
49-
<ExampleContainer>
50-
<DxcActionIcon icon="favorite" title="Favourite" />
51-
</ExampleContainer>
52-
</>
53-
);
18+
type ActionIconRowProps = {
19+
sizes?: ActionIconPropTypes["size"][];
20+
shapes?: ActionIconPropTypes["shape"][];
21+
colors?: ActionIconPropTypes["color"][];
22+
icon?: ActionIconPropTypes["icon"];
23+
statusModes?: Status["mode"][];
24+
statusPositions?: (Status["position"] | undefined)[];
25+
pseudoStates?: (PseudoState | "disabled" | undefined)[];
26+
groupBy?: GroupingKey[];
27+
};
28+
29+
const ActionIconRow = ({
30+
sizes = ["medium"],
31+
shapes = ["circle"],
32+
colors = ["neutral"],
33+
statusModes,
34+
statusPositions = [],
35+
pseudoStates = [],
36+
groupBy = ["size"],
37+
}: ActionIconRowProps) => {
38+
const getValuesForKey = (key?: GroupingKey) => {
39+
switch (key) {
40+
case "size":
41+
return sizes as string[];
42+
case "shape":
43+
return shapes as string[];
44+
case "color":
45+
return colors as string[];
46+
case "statusPosition":
47+
return statusPositions as string[];
48+
case "statusMode":
49+
return statusModes as string[];
50+
case "pseudoState":
51+
return pseudoStates;
52+
default:
53+
return [];
54+
}
55+
};
56+
57+
const renderGroup = (
58+
level: number,
59+
filters: {
60+
size?: ActionIconPropTypes["size"];
61+
shape?: ActionIconPropTypes["shape"];
62+
color?: ActionIconPropTypes["color"];
63+
statusMode?: Status["mode"];
64+
statusPosition?: Status["position"];
65+
pseudoState?: PseudoState | "disabled";
66+
}
67+
): JSX.Element | JSX.Element[] => {
68+
if (level >= groupBy.length) {
69+
const sizesToRender = filters.size ? [filters.size] : sizes;
70+
const colorsToRender = filters.color ? [filters.color] : colors;
71+
const shapesToRender = filters.shape ? [filters.shape] : shapes;
72+
const positionsToRender = filters.statusPosition
73+
? [filters.statusPosition]
74+
: statusPositions.length
75+
? statusPositions
76+
: [undefined];
77+
const modesToRender = filters.statusMode ? [filters.statusMode] : statusModes?.length ? statusModes : [undefined];
78+
79+
const pseudoStatesEnabled = !!filters.pseudoState;
80+
const isDisabled = filters.pseudoState === "disabled";
81+
const validPseudoState = isDisabled ? undefined : (filters.pseudoState as PseudoState | undefined);
82+
83+
return shapesToRender.map((shape) => (
84+
<DxcFlex
85+
key={`shape-${shape}-${String(filters.size ?? "all")}-${String(filters.color ?? "all")}`}
86+
gap="var(--spacing-gap-l)"
87+
wrap="wrap"
88+
>
89+
{sizesToRender.map((size) =>
90+
colorsToRender.map((color) =>
91+
positionsToRender.map((position) =>
92+
modesToRender.map((mode) => (
93+
<ExampleContainer
94+
key={`${size}-${shape}-${color}-${mode}-${position ?? "none"}`}
95+
pseudoState={validPseudoState}
96+
>
97+
<DxcActionIcon
98+
icon="settings"
99+
size={size}
100+
shape={shape}
101+
color={color}
102+
status={position && mode ? { position, mode: mode } : undefined}
103+
onClick={pseudoStatesEnabled ? () => console.log("") : undefined}
104+
disabled={isDisabled}
105+
/>
106+
</ExampleContainer>
107+
))
108+
)
109+
)
110+
)}
111+
</DxcFlex>
112+
));
113+
}
54114

55-
const NestedTooltip = () => (
115+
const key = groupBy[level];
116+
const values = getValuesForKey(key);
117+
118+
return values.map((value) => {
119+
const newFilters = { ...filters };
120+
if (key === "size") newFilters.size = value as ActionIconPropTypes["size"];
121+
else if (key === "shape") newFilters.shape = value as ActionIconPropTypes["shape"];
122+
else if (key === "color") newFilters.color = value as ActionIconPropTypes["color"];
123+
else if (key === "statusPosition") newFilters.statusPosition = value as Status["position"];
124+
else if (key === "statusMode") newFilters.statusMode = value as Status["mode"];
125+
else if (key === "pseudoState") newFilters.pseudoState = value as PseudoState | "disabled";
126+
127+
return (
128+
<div key={`${key}-${String(value)}`}>
129+
<Title title={String(value)} theme="light" level={3 + level} />
130+
{renderGroup(level + 1, newFilters)}
131+
</div>
132+
);
133+
});
134+
};
135+
136+
return <>{renderGroup(0, {})}</>;
137+
};
138+
139+
export const Shapes: Story = {
140+
render: () => (
141+
<>
142+
<Title title="Shapes" theme="light" level={2} />
143+
<ActionIconRow
144+
sizes={["xsmall", "small", "medium", "large", "xlarge", "xxlarge"]}
145+
shapes={["circle", "square"]}
146+
groupBy={["shape", "size"]}
147+
/>
148+
</>
149+
),
150+
};
151+
152+
export const Colors: Story = {
153+
render: () => (
154+
<>
155+
<Title title="Colors" theme="light" level={2} />
156+
<ActionIconRow
157+
sizes={["medium"]}
158+
shapes={["circle"]}
159+
colors={["neutral", "primary", "secondary", "tertiary", "success", "warning", "error", "info", "transparent"]}
160+
groupBy={["color"]}
161+
/>
162+
</>
163+
),
164+
};
165+
166+
export const Statuses: Story = {
167+
render: () => (
168+
<>
169+
<Title title="Statuses" theme="light" level={2} />
170+
<ActionIconRow
171+
sizes={["xsmall", "small", "medium", "large", "xlarge", "xxlarge"]}
172+
colors={["neutral", "primary", "secondary", "tertiary", "success", "warning", "error", "info", "transparent"]}
173+
shapes={["circle"]}
174+
statusModes={["default", "info", "success", "warning", "error"]}
175+
statusPositions={["top", "bottom"]}
176+
groupBy={["statusPosition", "statusMode", "color"]}
177+
/>
178+
</>
179+
),
180+
};
181+
182+
export const PseudoStates: Story = {
183+
render: () => (
184+
<>
185+
<Title title="Pseudo states" theme="light" level={2} />
186+
<ActionIconRow
187+
sizes={["xsmall", "small", "medium", "large", "xlarge", "xxlarge"]}
188+
shapes={["circle"]}
189+
statusModes={["success"]}
190+
statusPositions={[undefined, "top", "bottom"]}
191+
pseudoStates={[undefined, "pseudo-hover", "pseudo-focus", "pseudo-active", "disabled"]}
192+
groupBy={["pseudoState", "size"]}
193+
/>
194+
</>
195+
),
196+
};
197+
198+
export const Types: Story = {
199+
render: () => (
200+
<>
201+
<Title title="Icon (custom)" theme="light" level={2} />
202+
<ActionIconRow
203+
sizes={["xsmall", "small", "medium", "large", "xlarge", "xxlarge"]}
204+
shapes={["circle"]}
205+
groupBy={["size"]}
206+
/>
207+
<Title title="Icon (default)" theme="light" level={2} />
208+
<ActionIconRow
209+
sizes={["xsmall", "small", "medium", "large", "xlarge", "xxlarge"]}
210+
shapes={["circle"]}
211+
groupBy={["size"]}
212+
/>
213+
</>
214+
),
215+
};
216+
217+
const Tooltip = () => (
56218
<>
57-
<Title title="Nested tooltip" theme="light" level={2} />
219+
<Title title="Default tooltip" theme="ligth" level={2} />
58220
<ExampleContainer>
59-
<DxcInset top="var(--spacing-padding-xxl)">
60-
<DxcTooltip label="Favourite" position="top">
61-
<DxcActionIcon icon="favorite" title="Favourite" />
62-
</DxcTooltip>
63-
</DxcInset>
221+
<DxcActionIcon title="Home" icon="home" color="neutral" onClick={() => console.log()} />
64222
</ExampleContainer>
65223
</>
66224
);
67225

68-
type Story = StoryObj<typeof DxcActionIcon>;
69-
70-
export const Chromatic: Story = {
71-
render: ActionIcon,
72-
};
73-
74226
export const ActionIconTooltip: Story = {
75227
render: Tooltip,
76228
play: async ({ canvasElement }) => {
@@ -79,12 +231,3 @@ export const ActionIconTooltip: Story = {
79231
await userEvent.hover(button);
80232
},
81233
};
82-
83-
export const NestedActionIconTooltip: Story = {
84-
render: NestedTooltip,
85-
play: async ({ canvasElement }) => {
86-
const canvas = within(canvasElement);
87-
const button = await canvas.findByRole("button");
88-
await userEvent.hover(button);
89-
},
90-
};

0 commit comments

Comments
 (0)