🐛 Make Add to an organization a keyboard button (#12722)

"Add to an organization" in the team settings opens a modal but was an
<a> without href: the keyboard could not reach it. It is now a
<button type="button"> that keeps its text-link look (accent color,
medium weight, underline on hover, the same box), so Tab reaches it and
Enter or Space opens the modal.

New visual test "Shows the Add to an organization link", with fixtures
for a team in no organization and an organization the user belongs to:
0 px of difference at rest and on hover, and 0 px on the whole settings
page.

AI-assisted-by: claude-opus-5-5
This commit is contained in:
Eva Marco 2026-11-10 08:33:04 +01:00 committed by GitHub
parent cd1ece4beb
commit 72575e5c05
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 143 additions and 1 deletions

View File

@ -0,0 +1 @@
{ "~:authorized": true }

View File

@ -0,0 +1,78 @@
[
{
"~:features": {
"~#set": [
"layout/grid",
"styles/v2",
"fdata/pointer-map",
"fdata/objects-map",
"components/v2",
"fdata/shape-data-type"
]
},
"~:permissions": {
"~:type": "~:membership",
"~:is-owner": true,
"~:is-admin": true,
"~:can-edit": true
},
"~:name": "Default",
"~:modified-at": "~m1713533116375",
"~:id": "~uc7ce0794-0992-8105-8004-38e630f40f6d",
"~:created-at": "~m1713533116375",
"~:is-default": true
},
{
"~:features": {
"~#set": [
"layout/grid",
"styles/v2",
"fdata/pointer-map",
"fdata/objects-map",
"components/v2",
"fdata/shape-data-type"
]
},
"~:permissions": {
"~:type": "~:membership",
"~:is-owner": true,
"~:is-admin": true,
"~:can-edit": true
},
"~:name": "Second team",
"~:modified-at": "~m1701164272671",
"~:id": "~udd33ff88-f4e5-8033-8003-8096cc07bdf3",
"~:created-at": "~m1701164272671",
"~:is-default": false
},
{
"~:features": {
"~#set": [
"layout/grid",
"styles/v2",
"fdata/pointer-map",
"fdata/objects-map",
"components/v2",
"fdata/shape-data-type"
]
},
"~:permissions": {
"~:type": "~:membership",
"~:is-owner": true,
"~:is-admin": true,
"~:can-edit": true
},
"~:name": "Acme default",
"~:modified-at": "~m1713533116375",
"~:id": "~u11111111-1111-1111-1111-111111111111",
"~:created-at": "~m1713533116375",
"~:is-default": true,
"~:organization": {
"~:id": "~u22222222-2222-2222-2222-222222222222",
"~:name": "Acme",
"~:owner-id": "~u33333333-3333-3333-3333-333333333333",
"~:permissions": { "~:create-teams": "any" }
},
"~:organization-id": "~u22222222-2222-2222-2222-222222222222"
}
]

View File

@ -1064,3 +1064,42 @@ test.describe("Download report", () => {
await expect(page.getByText("Send", { exact: true })).toBeVisible();
});
});
test.describe("Team settings organization", () => {
test("Shows the Add to an organization link", async ({ page }) => {
const shot = { maxDiffPixelRatio: 0, threshold: 0 };
// Organizations need the admin console flag, a team the user owns that
// is in no organization, and an organization the user belongs to.
await DashboardPage.mockConfigFlags(page, [
"disable-onboarding",
"enable-admin-console",
]);
const dashboardPage = new DashboardPage(page);
await dashboardPage.setupDashboardFull();
await dashboardPage.mockRPC(
"get-teams",
"dashboard/get-teams-organization.json",
);
await dashboardPage.mockRPC(
/check-nitrate-sso/,
"dashboard/check-nitrate-sso-authorized.json",
);
await dashboardPage.mockRPC(
/get-environment-data/,
"dashboard/get-environment-data-empty.json",
);
await dashboardPage.mockRPC(
"get-team-stats?team-id=*",
"dashboard/get-team-stats.json",
);
await dashboardPage.goToSecondTeamSettingsSection();
const add = page.getByText("Add to an organization", { exact: true });
await expect(add).toBeVisible();
await page.mouse.move(10, 700);
await expect(add).toHaveScreenshot(shot);
await add.hover();
await expect(add).toHaveScreenshot(shot);
});
});

View File

@ -1721,7 +1721,10 @@
(when can-add-to-organization?
[:div {:class (stl/css :block-content)}
[:span {:class (stl/css :block-text)}
[:a {:on-click on-add-team-to-organization} (tr "dashboard.team-organization.add")]]])]))])
[:button {:type "button"
:class (stl/css :add-to-organization)
:on-click on-add-team-to-organization}
(tr "dashboard.team-organization.add")]]])]))])
[:div {:class (stl/css :block)}
[:div {:class (stl/css :block-label)}

View File

@ -56,6 +56,26 @@
text-wrap: nowrap;
}
// Opens the add to organization modal, so it is a button that keeps the
// look of a text link.
.add-to-organization {
font: inherit;
font-weight: 500;
// A button is a block in its line: the normal line height keeps the box of
// the inline link it replaces.
line-height: normal;
color: var(--color-accent-primary);
background: none;
border: none;
padding: 0;
cursor: pointer;
&:hover {
text-decoration: underline;
}
}
.block-content {
display: grid;
grid-template-columns: var(--sp-xxxl) 1fr;