User Credentials -> Draggable user credentials (#2131)
* draggable rows - wip * draggable rows - wip * draggable rows - wip * draggable rows - wip * draggable rows - wip * draggable rows - wip * added tests - wip * added test * draggable rows - wip * feedback * fixed position of reset credential btn * fixed position of reset credential btn * Remove unnecessary key props Co-authored-by: Agnieszka Gancarczyk <agancarc@redhat.com> Co-authored-by: Jon Koops <jonkoops@gmail.com>
This commit is contained in:
parent
a84701fe5c
commit
9d376ebcde
7 changed files with 305 additions and 19 deletions
|
@ -32,6 +32,7 @@ describe("User creation", () => {
|
||||||
let itemId = "user_crud";
|
let itemId = "user_crud";
|
||||||
let itemIdWithGroups = "user_with_groups_crud";
|
let itemIdWithGroups = "user_with_groups_crud";
|
||||||
let itemIdWithCred = "user_crud_cred";
|
let itemIdWithCred = "user_crud_cred";
|
||||||
|
const itemCredential = "Password";
|
||||||
|
|
||||||
before(() => {
|
before(() => {
|
||||||
for (let i = 0; i <= 2; i++) {
|
for (let i = 0; i <= 2; i++) {
|
||||||
|
@ -235,6 +236,38 @@ describe("User creation", () => {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("Edit credential label", () => {
|
||||||
|
listingPage.goToItemDetails(itemIdWithCred);
|
||||||
|
credentialsPage
|
||||||
|
.goToCredentialsTab()
|
||||||
|
.clickEditCredentialLabelBtn()
|
||||||
|
.fillEditCredentialForm()
|
||||||
|
.clickEditConfirmationBtn();
|
||||||
|
|
||||||
|
masthead.checkNotificationMessage(
|
||||||
|
"The user label has been changed successfully."
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Show credential data dialog", () => {
|
||||||
|
listingPage.goToItemDetails(itemIdWithCred);
|
||||||
|
credentialsPage
|
||||||
|
.goToCredentialsTab()
|
||||||
|
.clickShowDataDialogBtn()
|
||||||
|
.clickCloseDataDialogBtn();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Delete credential", () => {
|
||||||
|
listingPage.goToItemDetails(itemIdWithCred);
|
||||||
|
credentialsPage.goToCredentialsTab();
|
||||||
|
listingPage.deleteItem(itemCredential);
|
||||||
|
modalUtils.checkModalTitle("Delete credentials?").confirmModal();
|
||||||
|
|
||||||
|
masthead.checkNotificationMessage(
|
||||||
|
"The credentials has been deleted successfully."
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it("Delete user from search bar test", () => {
|
it("Delete user from search bar test", () => {
|
||||||
// Delete
|
// Delete
|
||||||
sidebarPage.waitForPageLoad();
|
sidebarPage.waitForPageLoad();
|
||||||
|
|
|
@ -17,6 +17,11 @@ export default class CredentialsPage {
|
||||||
"terms_and_conditions-option",
|
"terms_and_conditions-option",
|
||||||
];
|
];
|
||||||
private readonly confirmationButton = "confirm";
|
private readonly confirmationButton = "confirm";
|
||||||
|
private readonly editLabelBtn = "editUserLabelBtn";
|
||||||
|
private readonly labelField = "userLabelFld";
|
||||||
|
private readonly editConfirmationBtn = "editUserLabelAcceptBtn";
|
||||||
|
private readonly showDataDialogBtn = "showDataBtn";
|
||||||
|
private readonly closeDataDialogBtn = '[aria-label^="Close"]';
|
||||||
|
|
||||||
goToCredentialsTab() {
|
goToCredentialsTab() {
|
||||||
cy.findByTestId(this.credentialsTab).click();
|
cy.findByTestId(this.credentialsTab).click();
|
||||||
|
@ -81,4 +86,38 @@ export default class CredentialsPage {
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clickEditCredentialLabelBtn() {
|
||||||
|
cy.findByTestId(this.editLabelBtn)
|
||||||
|
.should("be.visible")
|
||||||
|
.click({ force: true });
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
fillEditCredentialForm() {
|
||||||
|
cy.findByTestId(this.labelField).focus().type("test");
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
clickEditConfirmationBtn() {
|
||||||
|
cy.findByTestId(this.editConfirmationBtn).click();
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
clickShowDataDialogBtn() {
|
||||||
|
cy.findByTestId(this.showDataDialogBtn)
|
||||||
|
.should("be.visible")
|
||||||
|
.click({ force: true });
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
clickCloseDataDialogBtn() {
|
||||||
|
cy.get(this.closeDataDialogBtn).eq(1).click({ force: true });
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { Fragment, useState } from "react";
|
import React, { Fragment, useMemo, useRef, useState } from "react";
|
||||||
import {
|
import {
|
||||||
AlertVariant,
|
AlertVariant,
|
||||||
Button,
|
Button,
|
||||||
|
@ -24,8 +24,8 @@ import type CredentialRepresentation from "@keycloak/keycloak-admin-client/lib/d
|
||||||
import { ResetPasswordDialog } from "./user-credentials/ResetPasswordDialog";
|
import { ResetPasswordDialog } from "./user-credentials/ResetPasswordDialog";
|
||||||
import { ResetCredentialDialog } from "./user-credentials/ResetCredentialDialog";
|
import { ResetCredentialDialog } from "./user-credentials/ResetCredentialDialog";
|
||||||
import { InlineLabelEdit } from "./user-credentials/InlineLabelEdit";
|
import { InlineLabelEdit } from "./user-credentials/InlineLabelEdit";
|
||||||
|
|
||||||
import "./user-credentials.css";
|
import "./user-credentials.css";
|
||||||
|
import styles from "@patternfly/react-styles/css/components/Table/table";
|
||||||
import { CredentialRow } from "./user-credentials/CredentialRow";
|
import { CredentialRow } from "./user-credentials/CredentialRow";
|
||||||
import { toUpperCase } from "../util";
|
import { toUpperCase } from "../util";
|
||||||
|
|
||||||
|
@ -61,6 +61,14 @@ export const UserCredentials = ({ user }: UserCredentialsProps) => {
|
||||||
rowKey: string;
|
rowKey: string;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
const bodyRef = useRef<HTMLTableSectionElement>(null);
|
||||||
|
const [state, setState] = useState({
|
||||||
|
draggedItemId: "",
|
||||||
|
draggingToItemIndex: -1,
|
||||||
|
dragging: false,
|
||||||
|
tempItemOrder: [""],
|
||||||
|
});
|
||||||
|
|
||||||
useFetch(
|
useFetch(
|
||||||
() => adminClient.users.getCredentials({ id: user.id! }),
|
() => adminClient.users.getCredentials({ id: user.id! }),
|
||||||
(credentials) => {
|
(credentials) => {
|
||||||
|
@ -150,6 +158,165 @@ export const UserCredentials = ({ user }: UserCredentialsProps) => {
|
||||||
/>
|
/>
|
||||||
</CredentialRow>
|
</CredentialRow>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const itemOrder = useMemo(
|
||||||
|
() =>
|
||||||
|
groupedUserCredentials.map(({ value }) =>
|
||||||
|
value.map(({ id }) => id).toString()
|
||||||
|
),
|
||||||
|
[groupedUserCredentials]
|
||||||
|
);
|
||||||
|
|
||||||
|
const onDragStart = (evt: React.DragEvent) => {
|
||||||
|
evt.dataTransfer.effectAllowed = "move";
|
||||||
|
evt.dataTransfer.setData("text/plain", evt.currentTarget.id);
|
||||||
|
const draggedItemId = evt.currentTarget.id;
|
||||||
|
evt.currentTarget.classList.add(styles.modifiers.ghostRow);
|
||||||
|
evt.currentTarget.setAttribute("aria-pressed", "true");
|
||||||
|
setState({ ...state, draggedItemId, dragging: true });
|
||||||
|
};
|
||||||
|
|
||||||
|
const moveItem = (items: string[], targetItem: string, toIndex: number) => {
|
||||||
|
const fromIndex = items.indexOf(targetItem);
|
||||||
|
if (fromIndex === toIndex) {
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
const result = [...items];
|
||||||
|
result.splice(toIndex, 0, result.splice(fromIndex, 1)[0]);
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
const move = (itemOrder: string[]) => {
|
||||||
|
if (!bodyRef.current) return;
|
||||||
|
const ulNode = bodyRef.current;
|
||||||
|
const nodes = Array.from(ulNode.children);
|
||||||
|
if (nodes.every(({ id }, i) => id === itemOrder[i])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ulNode.replaceChildren();
|
||||||
|
itemOrder.forEach((itemId) => {
|
||||||
|
ulNode.appendChild(nodes.find(({ id }) => id === itemId)!);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onDragCancel = () => {
|
||||||
|
if (!bodyRef.current) return;
|
||||||
|
Array.from(bodyRef.current.children).forEach((el) => {
|
||||||
|
el.classList.remove(styles.modifiers.ghostRow);
|
||||||
|
el.setAttribute("aria-pressed", "false");
|
||||||
|
});
|
||||||
|
setState({
|
||||||
|
...state,
|
||||||
|
draggedItemId: "",
|
||||||
|
draggingToItemIndex: -1,
|
||||||
|
dragging: false,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onDragLeave = (evt: React.DragEvent) => {
|
||||||
|
if (!isValidDrop(evt)) {
|
||||||
|
move(itemOrder);
|
||||||
|
setState({ ...state, draggingToItemIndex: -1 });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const isValidDrop = (evt: React.DragEvent) => {
|
||||||
|
if (!bodyRef.current) return false;
|
||||||
|
const ulRect = bodyRef.current.getBoundingClientRect();
|
||||||
|
return (
|
||||||
|
evt.clientX > ulRect.x &&
|
||||||
|
evt.clientX < ulRect.x + ulRect.width &&
|
||||||
|
evt.clientY > ulRect.y &&
|
||||||
|
evt.clientY < ulRect.y + ulRect.height
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onDrop = (evt: React.DragEvent) => {
|
||||||
|
if (isValidDrop(evt)) {
|
||||||
|
onDragFinish(state.draggedItemId, state.tempItemOrder);
|
||||||
|
} else {
|
||||||
|
onDragCancel();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onDragOver = (evt: React.DragEvent) => {
|
||||||
|
evt.preventDefault();
|
||||||
|
const td = evt.target as HTMLTableCellElement;
|
||||||
|
const curListItem = td.closest("tr");
|
||||||
|
if (
|
||||||
|
!curListItem ||
|
||||||
|
(bodyRef.current && !bodyRef.current.contains(curListItem)) ||
|
||||||
|
curListItem.id === state.draggedItemId
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
const dragId = curListItem.id;
|
||||||
|
const draggingToItemIndex = Array.from(
|
||||||
|
bodyRef.current?.children || []
|
||||||
|
).findIndex((item) => item.id === dragId);
|
||||||
|
if (draggingToItemIndex === state.draggingToItemIndex) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const tempItemOrder = moveItem(
|
||||||
|
itemOrder,
|
||||||
|
state.draggedItemId,
|
||||||
|
draggingToItemIndex
|
||||||
|
);
|
||||||
|
move(tempItemOrder);
|
||||||
|
setState({
|
||||||
|
...state,
|
||||||
|
draggingToItemIndex,
|
||||||
|
tempItemOrder,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onDragEnd = ({ target }: React.DragEvent) => {
|
||||||
|
if (!(target instanceof HTMLTableRowElement)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
target.classList.remove(styles.modifiers.ghostRow);
|
||||||
|
target.setAttribute("aria-pressed", "false");
|
||||||
|
setState({
|
||||||
|
...state,
|
||||||
|
draggedItemId: "",
|
||||||
|
draggingToItemIndex: -1,
|
||||||
|
dragging: false,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onDragFinish = async (dragged: string, newOrder: string[]) => {
|
||||||
|
dragged = dragged.split(",")[0];
|
||||||
|
const keys = groupedUserCredentials.map(({ value }) =>
|
||||||
|
value.map(({ id }) => id)
|
||||||
|
);
|
||||||
|
const oldIndex = keys.findIndex((el) => el.join().includes(dragged));
|
||||||
|
const newIndex = newOrder.findIndex((el) => el.includes(dragged));
|
||||||
|
const times = newIndex - oldIndex;
|
||||||
|
|
||||||
|
try {
|
||||||
|
for (let index = 0; index < Math.abs(times); index++) {
|
||||||
|
if (times > 0) {
|
||||||
|
await adminClient.users.moveCredentialPositionDown({
|
||||||
|
id: user.id!,
|
||||||
|
credentialId: dragged,
|
||||||
|
newPreviousCredentialId: `${keys[newIndex][0]}`,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await adminClient.users.moveCredentialPositionUp({
|
||||||
|
id: user.id!,
|
||||||
|
credentialId: dragged,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
refresh();
|
||||||
|
addAlert(t("users:updatedCredentialMoveSuccess"), AlertVariant.success);
|
||||||
|
} catch (error) {
|
||||||
|
addError("users:updatedCredentialMoveError", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{isOpen && (
|
{isOpen && (
|
||||||
|
@ -170,7 +337,6 @@ export const UserCredentials = ({ user }: UserCredentialsProps) => {
|
||||||
{userCredentials.length !== 0 && passwordTypeFinder === undefined && (
|
{userCredentials.length !== 0 && passwordTypeFinder === undefined && (
|
||||||
<>
|
<>
|
||||||
<Button
|
<Button
|
||||||
key={`confirmSaveBtn-table-${user.id}`}
|
|
||||||
className="kc-setPasswordBtn-tbl"
|
className="kc-setPasswordBtn-tbl"
|
||||||
data-testid="setPasswordBtn-table"
|
data-testid="setPasswordBtn-table"
|
||||||
variant="primary"
|
variant="primary"
|
||||||
|
@ -188,7 +354,7 @@ export const UserCredentials = ({ user }: UserCredentialsProps) => {
|
||||||
<>
|
<>
|
||||||
{user.email && (
|
{user.email && (
|
||||||
<Button
|
<Button
|
||||||
className="resetCredentialBtn-header"
|
className="kc-resetCredentialBtn-header"
|
||||||
variant="primary"
|
variant="primary"
|
||||||
data-testid="credentialResetBtn"
|
data-testid="credentialResetBtn"
|
||||||
onClick={() => setOpenCredentialReset(true)}
|
onClick={() => setOpenCredentialReset(true)}
|
||||||
|
@ -196,15 +362,19 @@ export const UserCredentials = ({ user }: UserCredentialsProps) => {
|
||||||
{t("credentialResetBtn")}
|
{t("credentialResetBtn")}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
<TableComposable aria-label="password-data-table" variant={"compact"}>
|
<TableComposable
|
||||||
|
aria-label="userCredentials-table"
|
||||||
|
variant={"compact"}
|
||||||
|
>
|
||||||
<Thead>
|
<Thead>
|
||||||
<Tr>
|
<Tr className="kc-table-header">
|
||||||
<Th>
|
<Th>
|
||||||
<HelpItem
|
<HelpItem
|
||||||
helpText="users:userCredentialsHelpText"
|
helpText="users:userCredentialsHelpText"
|
||||||
fieldLabelId="users:userCredentialsHelpTextLabel"
|
fieldLabelId="users:userCredentialsHelpTextLabel"
|
||||||
/>
|
/>
|
||||||
</Th>
|
</Th>
|
||||||
|
<Th />
|
||||||
<Th>{t("type")}</Th>
|
<Th>{t("type")}</Th>
|
||||||
<Th>{t("userLabel")}</Th>
|
<Th>{t("userLabel")}</Th>
|
||||||
<Th>{t("data")}</Th>
|
<Th>{t("data")}</Th>
|
||||||
|
@ -212,10 +382,28 @@ export const UserCredentials = ({ user }: UserCredentialsProps) => {
|
||||||
<Th />
|
<Th />
|
||||||
</Tr>
|
</Tr>
|
||||||
</Thead>
|
</Thead>
|
||||||
<Tbody>
|
<Tbody
|
||||||
|
ref={bodyRef}
|
||||||
|
onDragOver={onDragOver}
|
||||||
|
onDrop={onDragOver}
|
||||||
|
onDragLeave={onDragLeave}
|
||||||
|
>
|
||||||
{groupedUserCredentials.map((groupedCredential, rowIndex) => (
|
{groupedUserCredentials.map((groupedCredential, rowIndex) => (
|
||||||
<Fragment key={`table-${groupedCredential.key}`}>
|
<Fragment key={groupedCredential.key}>
|
||||||
<Tr>
|
<Tr
|
||||||
|
id={groupedCredential.value.map(({ id }) => id).toString()}
|
||||||
|
draggable
|
||||||
|
onDrop={onDrop}
|
||||||
|
onDragEnd={onDragEnd}
|
||||||
|
onDragStart={onDragStart}
|
||||||
|
>
|
||||||
|
<Td
|
||||||
|
draggableRow={{
|
||||||
|
id: `draggable-row-${groupedCredential.value.map(
|
||||||
|
({ id }) => id
|
||||||
|
)}`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
{groupedCredential.value.length > 1 ? (
|
{groupedCredential.value.length > 1 ? (
|
||||||
<Td
|
<Td
|
||||||
className="kc-expandRow-btn"
|
className="kc-expandRow-btn"
|
||||||
|
@ -240,24 +428,36 @@ export const UserCredentials = ({ user }: UserCredentialsProps) => {
|
||||||
<Td />
|
<Td />
|
||||||
)}
|
)}
|
||||||
<Td
|
<Td
|
||||||
key={`table-item-${groupedCredential.key}`}
|
|
||||||
dataLabel={`columns-${groupedCredential.key}`}
|
dataLabel={`columns-${groupedCredential.key}`}
|
||||||
className="kc-notExpandableRow-credentialType"
|
className="kc-notExpandableRow-credentialType"
|
||||||
|
data-testid="credentialType"
|
||||||
>
|
>
|
||||||
{toUpperCase(groupedCredential.key)}
|
{toUpperCase(groupedCredential.key)}
|
||||||
</Td>
|
</Td>
|
||||||
{groupedCredential.value.length <= 1 &&
|
{groupedCredential.value.length <= 1 &&
|
||||||
groupedCredential.value.map((credential) => (
|
groupedCredential.value.map((credential) => (
|
||||||
<Row
|
<Row key={credential.id} credential={credential} />
|
||||||
key={`subrow-${credential.id}`}
|
|
||||||
credential={credential}
|
|
||||||
/>
|
|
||||||
))}
|
))}
|
||||||
</Tr>
|
</Tr>
|
||||||
{groupedCredential.isExpanded &&
|
{groupedCredential.isExpanded &&
|
||||||
groupedCredential.value.map((credential) => (
|
groupedCredential.value.map((credential) => (
|
||||||
<Tr key={`child-key-${credential.id}`}>
|
<Tr
|
||||||
|
key={credential.id}
|
||||||
|
id={credential.id}
|
||||||
|
draggable
|
||||||
|
onDrop={onDrop}
|
||||||
|
onDragEnd={onDragEnd}
|
||||||
|
onDragStart={onDragStart}
|
||||||
|
>
|
||||||
<Td />
|
<Td />
|
||||||
|
<Td
|
||||||
|
className="kc-draggable-dropdown-type-icon"
|
||||||
|
draggableRow={{
|
||||||
|
id: `draggable-row-${groupedCredential.value.map(
|
||||||
|
({ id }) => id
|
||||||
|
)}`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
<Td
|
<Td
|
||||||
dataLabel={`child-columns-${credential.id}`}
|
dataLabel={`child-columns-${credential.id}`}
|
||||||
className="kc-expandableRow-credentialType"
|
className="kc-expandableRow-credentialType"
|
||||||
|
|
|
@ -144,6 +144,10 @@ export default {
|
||||||
deleteCredentialsSuccess: "The credentials has been deleted successfully.",
|
deleteCredentialsSuccess: "The credentials has been deleted successfully.",
|
||||||
deleteCredentialsError: "Error deleting users credentials: {{error}}",
|
deleteCredentialsError: "Error deleting users credentials: {{error}}",
|
||||||
deleteBtn: "Delete",
|
deleteBtn: "Delete",
|
||||||
|
updatedCredentialMoveSuccess:
|
||||||
|
"User Credential configuration has been saved",
|
||||||
|
updatedCredentialMoveError:
|
||||||
|
"User Credential configuration hasn't been saved",
|
||||||
resetPasswordFor: "Reset password for {{username}}",
|
resetPasswordFor: "Reset password for {{username}}",
|
||||||
resetPasswordConfirm: "Reset password?",
|
resetPasswordConfirm: "Reset password?",
|
||||||
resetPasswordConfirmText:
|
resetPasswordConfirmText:
|
||||||
|
|
|
@ -67,3 +67,12 @@
|
||||||
.kc-temporaryPassword {
|
.kc-temporaryPassword {
|
||||||
margin: 6px 0 10px 35px;
|
margin: 6px 0 10px 35px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.kc-resetCredentialBtn-header {
|
||||||
|
float: right;
|
||||||
|
margin: 20px 40px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr.kc-table-header th {
|
||||||
|
padding-top: 0px !important;
|
||||||
|
}
|
|
@ -22,6 +22,7 @@ export const CredentialDataDialog = ({
|
||||||
<Modal
|
<Modal
|
||||||
variant={ModalVariant.medium}
|
variant={ModalVariant.medium}
|
||||||
title={t("passwordDataTitle")}
|
title={t("passwordDataTitle")}
|
||||||
|
data-testid="passwordDataDialog"
|
||||||
isOpen
|
isOpen
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
>
|
>
|
||||||
|
|
|
@ -61,25 +61,25 @@ export const InlineLabelEdit = ({
|
||||||
<>
|
<>
|
||||||
<TextInput
|
<TextInput
|
||||||
name="userLabel"
|
name="userLabel"
|
||||||
|
data-testid="userLabelFld"
|
||||||
defaultValue={credential.userLabel}
|
defaultValue={credential.userLabel}
|
||||||
ref={register()}
|
ref={register()}
|
||||||
type="text"
|
type="text"
|
||||||
className="kc-userLabel"
|
className="kc-userLabel"
|
||||||
aria-label={t("userLabel")}
|
aria-label={t("userLabel")}
|
||||||
data-testid="user-label-fld"
|
|
||||||
/>
|
/>
|
||||||
<div className="kc-userLabel-actionBtns">
|
<div className="kc-userLabel-actionBtns">
|
||||||
<Button
|
<Button
|
||||||
data-testid="editUserLabel-acceptBtn"
|
data-testid="editUserLabelAcceptBtn"
|
||||||
variant="link"
|
variant="link"
|
||||||
className="kc-editUserLabel-acceptBtn"
|
className="kc-editUserLabelAcceptBtn"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
handleSubmit(saveUserLabel)();
|
handleSubmit(saveUserLabel)();
|
||||||
}}
|
}}
|
||||||
icon={<CheckIcon />}
|
icon={<CheckIcon />}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
data-testid="editUserLabel-cancelBtn"
|
data-testid="editUserLabelCancelBtn"
|
||||||
variant="link"
|
variant="link"
|
||||||
className="kc-editUserLabel-cancelBtn"
|
className="kc-editUserLabel-cancelBtn"
|
||||||
onClick={toggle}
|
onClick={toggle}
|
||||||
|
|
Loading…
Reference in a new issue