remove all use of deprecated Select and Dropdown (#29270)

* removed deprecated select

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* some more deprecation removal

working towards fixing: #28197

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* fixed tests

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* changed to use new api

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* more deprecation removal

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* fixed merge error

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* fix tests

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* small fix

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* fixed tests

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* fixed merge error

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* no more default text for SelectOption

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* fixed tests

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* fixed tests

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* fixed test

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* fixed test

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* fixed tests

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* fixed tests

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* fixed test

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* fixed tests

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* fixed test

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* fixed tests

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* fixed tests

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* fixed tests

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* changed to use id

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* fixed dropdown in keycloakCard and test fixes

Signed-off-by: mfrances <mfrances@redhat.com>
Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* fixed lint error

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* fixed test

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* fixed tests

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* fix dropdown/select related test failures

Signed-off-by: mfrances <mfrances@redhat.com>

* fixed test

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* fixed test

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* i18n label

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* fix test

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* fixed tests

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* fixed tests

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* fixed test

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* removed

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* fixed merge error

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

---------

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>
Signed-off-by: mfrances <mfrances@redhat.com>
Co-authored-by: mfrances <mfrances@redhat.com>
This commit is contained in:
Erik Jan de Wit 2024-05-30 13:45:58 +02:00 committed by GitHub
parent e0507eb762
commit 5949fd43d0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
142 changed files with 2843 additions and 2432 deletions

View file

@ -85,7 +85,7 @@ export const EditTheResource = ({
id={`permissions-${p.id}`}
name={`permissions.${index}.scopes`}
label="permissions"
variant="typeaheadmulti"
variant="typeaheadMulti"
controller={{ defaultValue: [] }}
options={resource.scopes.map(({ name, displayName }) => ({
key: name,

View file

@ -195,7 +195,7 @@ export const ShareTheResource = ({
<FormGroup label="" fieldId="permissions-selected">
<SelectControl
name="permissions"
variant="typeaheadmulti"
variant="typeaheadMulti"
controller={{ defaultValue: [] }}
options={resource.scopes.map(({ name, displayName }) => ({
key: name,

View file

@ -192,11 +192,9 @@ describe("Client Scopes test", () => {
.checkDropdownItemIsDisabled("Delete")
.clickItemCheckbox(itemName)
.checkInSearchBarChangeTypeToButtonIsDisabled(false)
.clickSearchBarActionButton()
.checkDropdownItemIsDisabled("Delete", false)
.clickItemCheckbox(itemName)
.checkInSearchBarChangeTypeToButtonIsDisabled()
.clickSearchBarActionButton()
.checkDropdownItemIsDisabled("Delete");
});

View file

@ -321,8 +321,8 @@ describe("Clients test", () => {
clientDetailsPage.goToClientScopesEvaluateGeneratedUserInfoTab();
cy.get("div#generatedUserInfo").contains("No generated user info");
cy.get("input#user-select-typeahead").type("admin-a");
cy.get("li[id*=select-option-] > button:first-child").click();
cy.get("[data-testid='user'] input").type("admin-a");
cy.get(".pf-v5-c-menu__item-text").click();
clientDetailsPage.goToClientScopesEvaluateGeneratedAccessTokenTab();
cy.get("div#generatedAccessToken").contains(

View file

@ -412,7 +412,6 @@ describe("Realm settings events tab tests", () => {
});
it("Should remove all events from event listener and re-save original", () => {
realmSettingsPage.shouldSaveEventListener();
realmSettingsPage.shouldRemoveAllEventListeners();
realmSettingsPage.shouldReSaveEventListener();
});

View file

@ -204,14 +204,14 @@ describe("Realm settings tabs tests", () => {
realmSettingsPage.goToLocalizationTab();
realmSettingsPage.goToLocalizationLocalesSubTab();
cy.findByTestId("internationalization-disabled").click({ force: true });
cy.findByTestId("internationalizationEnabled").click({ force: true });
cy.get(realmSettingsPage.supportedLocalesTypeahead)
.click()
.get(".pf-v5-c-select__menu-item")
.get(".pf-v5-c-menu__list-item")
.contains("Danish")
.click();
cy.get("#kc-l-supported-locales").click();
.click({ force: true });
cy.findByTestId("internationalizationEnabled").click({ force: true });
cy.intercept("GET", `/admin/realms/${realmName}/localization/en*`).as(
"load",
@ -258,7 +258,7 @@ describe("Realm settings tabs tests", () => {
.should("be.visible");
cy.findByTestId("selectAll").click();
cy.get('[data-testid="toolbar-deleteBtn"] button').click();
cy.get('[data-testid="toolbar-deleteBtn"]').click();
cy.findByTestId("delete-selected-TranslationBtn").click();
cy.findByTestId("confirm").click();
masthead.checkNotificationMessage("Successfully removed translation(s).");

View file

@ -438,7 +438,7 @@ describe("User creation", () => {
cy.get("table")
.contains(itemCredential)
.parentsUntil("tbody")
.find(".pf-v5-c-dropdown__toggle")
.find(".pf-v5-c-table__action .pf-v5-c-menu-toggle")
.click();
cy.get("table").contains("Delete").click();
modalUtils.checkModalTitle("Delete credentials?").confirmModal();

View file

@ -15,6 +15,6 @@ export default class Select {
}
static #getSelectMenu(chain: Cypress.Chainable<JQuery<HTMLElement>>) {
return chain.parent().get(".pf-v5-c-select__menu");
return chain.parent().get(".pf-v5-c-menu__list");
}
}

View file

@ -18,16 +18,14 @@ export default class CommonElements {
this.secondaryBtn = this.parentSelector + ".pf-v5-c-button.pf-m-secondary";
this.secondaryBtnLink = this.parentSelector + ".pf-v5-c-button.pf-m-link";
this.dropdownMenuItem =
this.parentSelector +
".pf-v5-c-dropdown__menu .pf-v5-c-dropdown__menu-item";
this.parentSelector + ".pf-v5-c-menu__list .pf-v5-c-menu__item";
this.selectMenuItem =
this.parentSelector + ".pf-v5-c-select__menu .pf-v5-c-select__menu-item";
this.dropdownToggleBtn = this.parentSelector + ".pf-v5-c-select__toggle";
this.tableKebabBtn = this.parentSelector + ".pf-v5-c-dropdown__toggle";
this.dropdownSelectToggleBtn =
this.parentSelector + ".pf-v5-c-select__toggle";
this.parentSelector + ".pf-v5-c-menu__list .pf-v5-c-menu__list-item";
this.dropdownToggleBtn = this.parentSelector + ".pf-v5-c-menu-toggle";
this.tableKebabBtn = this.parentSelector + ".pf-v5-c-menu-toggle";
this.dropdownSelectToggleBtn = this.parentSelector + ".pf-v5-c-menu-toggle";
this.dropdownSelectToggleItem =
this.parentSelector + ".pf-v5-c-select__menu > li";
this.parentSelector + ".pf-v5-c-menu__list > li";
}
clickPrimaryBtn() {

View file

@ -28,14 +28,14 @@ export enum FilterSession {
}
export default class ListingPage extends CommonElements {
#tableToolbar = ".pf-v5-c-toolbar";
#tableToolbar = "section .pf-v5-c-toolbar";
#itemsRows = "table:visible";
#deleteUserButton = "delete-user-btn";
#emptyListImg = '[role="tabpanel"]:not([hidden]) [data-testid="empty-state"]';
#emptyState = "empty-state";
#itemRowDrpDwn = ".pf-v5-c-menu-toggle";
#itemRowSelect = ".pf-v5-c-select__toggle:nth-child(1)";
#itemRowSelectItem = ".pf-v5-c-select__menu-item";
#itemRowDrpDwn = ".pf-v5-c-table__action button";
#itemRowSelect = "[data-testid='cell-dropdown']";
#itemRowSelectItem = ".pf-v5-c-menu__item";
#itemCheckbox = ".pf-v5-c-table__check";
public exportBtn = '[role="menuitem"]:nth-child(1)';
public deleteBtn = '[role="menuitem"]:nth-child(2)';
@ -52,10 +52,11 @@ export default class ListingPage extends CommonElements {
public tableRowItem = "tbody tr[data-ouia-component-type]:visible";
#table = "table[aria-label]";
#filterSessionDropdownButton = ".pf-v5-c-select button:nth-child(1)";
#filterDropdownButton = "[class*='searchtype'] button";
#kebabMenu = ".pf-v5-c-dropdown__toggle";
#dropdownItem = ".pf-v5-c-dropdown__menu-item";
#changeTypeToButton = ".pf-v5-c-select__toggle";
#searchTypeButton = "[data-testid='clientScopeSearch']";
#filterDropdownButton = "[data-testid='clientScopeSearchType']";
#protocolFilterDropdownButton = "[data-testid='clientScopeSearchProtocol']";
#kebabMenu = "[data-testid='kebab']";
#dropdownItem = ".pf-v5-c-menu__list-item";
#toolbarChangeType = "#change-type-dropdown";
#tableNameColumnPrefix = "name-column-";
#rowGroup = "table:visible tbody[role='rowgroup']";
@ -139,7 +140,7 @@ export default class ListingPage extends CommonElements {
}
clickSearchBarActionButton() {
cy.get(this.#tableToolbar).find(this.#kebabMenu).last().click();
cy.get(this.#tableToolbar).find(this.#kebabMenu).click();
return this;
}
@ -320,14 +321,14 @@ export default class ListingPage extends CommonElements {
}
selectFilter(filter: Filter) {
cy.get(this.#filterDropdownButton).first().click();
cy.get(this.#searchTypeButton).click();
cy.get(this.#dropdownItem).contains(filter).click();
return this;
}
selectSecondaryFilter(itemName: string) {
cy.get(this.#filterDropdownButton).last().click();
cy.get(this.#filterDropdownButton).click();
cy.get(this.#itemRowSelectItem).contains(itemName).click();
return this;
@ -340,7 +341,8 @@ export default class ListingPage extends CommonElements {
}
selectSecondaryFilterProtocol(protocol: FilterProtocol) {
this.selectSecondaryFilter(protocol);
cy.get(this.#protocolFilterDropdownButton).click();
cy.get(this.#itemRowSelectItem).contains(protocol).click();
return this;
}
@ -364,14 +366,14 @@ export default class ListingPage extends CommonElements {
cy.get(this.#itemsRows)
.contains(itemName)
.parentsUntil("tbody")
.find(this.#changeTypeToButton)
.find(this.#toolbarChangeType)
.first()
.click();
cy.get(this.#itemsRows)
.contains(itemName)
.parentsUntil("tbody")
.find(this.#changeTypeToButton)
.find(this.#toolbarChangeType)
.contains(assignedType)
.click();
@ -383,7 +385,7 @@ export default class ListingPage extends CommonElements {
if (!disabled) {
condition = "be.enabled";
}
cy.get(this.#changeTypeToButton).first().should(condition);
cy.get(this.#toolbarChangeType).first().should(condition);
return this;
}
@ -391,7 +393,7 @@ export default class ListingPage extends CommonElements {
checkDropdownItemIsDisabled(itemName: string, disabled: boolean = true) {
cy.get(this.#dropdownItem)
.contains(itemName)
.should("have.attr", "aria-disabled", String(disabled));
.should((disabled ? "" : "not.") + "be.disabled");
return this;
}

View file

@ -17,7 +17,7 @@ export default class SidebarPage extends CommonElements {
#identityProvidersBtn = "#nav-item-identity-providers";
#userFederationBtn = "#nav-item-user-federation";
realmsElements = '[data-testid="realmSelector"] li';
realmsElements = '[id="realm-select"] li';
showCurrentRealms(length: number) {
cy.findByTestId(this.#realmsDrpDwn).click();

View file

@ -1,11 +1,11 @@
export default class PageObject {
#selectItemSelectedIcon = ".pf-v5-c-select__menu-item-icon";
#drpDwnMenuList = ".pf-v5-c-dropdown__menu";
#drpDwnMenuItem = ".pf-v5-c-dropdown__menu-item";
#drpDwnMenuToggleBtn = ".pf-v5-c-select__toggle";
#selectMenuList = ".pf-v5-c-select__menu";
#selectMenuItem = ".pf-v5-c-select__menu-item";
#selectMenuToggleBtn = ".pf-v5-c-select__toggle";
#selectItemSelectedIcon = ".pf-v5-c-menu-toggle__toggle-icon";
#drpDwnMenuList = ".pf-v5-c-menu__list";
#drpDwnMenuItem = ".pf-v5-c-menu__item";
#drpDwnMenuToggleBtn = ".pf-v5-c-menu-toggle";
#selectMenuList = ".pf-v5-c-menu__list";
#selectMenuItem = ".pf-v5-c-menu__list-item";
#selectMenuToggleBtn = ".pf-v5-c-menu-toggle";
#switchInput = ".pf-v5-c-switch__input";
#formLabel = ".pf-v5-c-form__label";
#chipGroup = ".pf-v5-c-chip-group";
@ -103,7 +103,12 @@ export default class PageObject {
) {
element =
element ??
cy.get(this.#drpDwnMenuToggleBtn).contains(itemName).parent().parent();
cy
.get(this.#drpDwnMenuToggleBtn)
.parent()
.contains(itemName)
.parent()
.parent();
element.click();
return this;
}
@ -114,7 +119,12 @@ export default class PageObject {
) {
element =
element ??
cy.get(this.#drpDwnMenuToggleBtn).contains(itemName).parent().parent();
cy
.get(this.#drpDwnMenuToggleBtn)
.parent()
.contains(itemName)
.parent()
.parent();
this.clickDropdownMenuToggleButton(itemName, element);
this.assertDropdownMenuIsOpen(true);
return this;
@ -126,7 +136,12 @@ export default class PageObject {
) {
element =
element ??
cy.get(this.#drpDwnMenuToggleBtn).contains(itemName).parent().parent();
cy
.get(this.#drpDwnMenuToggleBtn)
.parent()
.contains(itemName)
.parent()
.parent();
this.clickDropdownMenuToggleButton(itemName, element);
this.assertDropdownMenuIsOpen(false);
return this;

View file

@ -61,7 +61,7 @@ export default class TablePage extends CommonElements {
)
.contains(itemName)
.parentsUntil("tbody")
.find(".pf-v5-c-menu-toggle")
.find(".pf-v5-c-table__action .pf-v5-c-menu-toggle")
.click()
.get(this.#tableKebabMenu)
.contains(actionItemName);

View file

@ -8,7 +8,6 @@ export default class TableToolbar extends CommonElements {
#nextPageBtn: string;
#previousPageBtn: string;
#searchTypeDropdownBtn: string;
#searchTypeSelectToggleBtn: string;
#actionToggleBtn: string;
constructor() {
@ -21,12 +20,8 @@ export default class TableToolbar extends CommonElements {
this.#nextPageBtn = this.parentSelector + "button[data-action=next]";
this.#previousPageBtn =
this.parentSelector + "button[data-action=previous]";
this.#searchTypeDropdownBtn =
this.parentSelector +
".pf-v5-c-dropdown .keycloak__client-scopes__searchtype";
this.#searchTypeSelectToggleBtn =
this.parentSelector + "[class*='searchtype'] .pf-v5-c-select__toggle";
this.#actionToggleBtn = this.tableKebabBtn + "[aria-label='Actions']";
this.#searchTypeDropdownBtn = "[data-testid='clientScopeSearchType']";
this.#actionToggleBtn = "[data-testid='kebab']";
}
clickNextPageButton(isUpperButton = true) {
@ -98,7 +93,7 @@ export default class TableToolbar extends CommonElements {
}
selectSecondarySearchType(itemName: FilterAssignedType) {
cy.get(this.#searchTypeSelectToggleBtn).click();
cy.get(this.#searchTypeDropdownBtn).click();
cy.get(this.dropdownSelectToggleItem).contains(itemName).click();
return this;
}

View file

@ -1,8 +1,8 @@
import ModalUtils from "../../../../util/ModalUtils";
export default class BindFlowModal extends ModalUtils {
#bindingType = "#bindingType";
#dropdownSelectToggleItem = ".pf-v5-c-select__menu > li";
#bindingType = "#chooseBindingType";
#dropdownSelectToggleItem = ".pf-v5-c-menu__list > li";
fill(bindingType: string) {
cy.get(this.#bindingType).click();

View file

@ -40,6 +40,7 @@ export default class FlowDetails {
.parentsUntil(".keycloak__authentication__flow-row")
.find(".keycloak__authentication__requirement-dropdown")
.click()
.parent()
.contains(requirement)
.click();
return this;
@ -53,6 +54,7 @@ export default class FlowDetails {
#clickEditDropdownForFlow(subFlowName: string, option: string) {
cy.findByTestId(`${subFlowName}-edit-dropdown`)
.click()
.parent()
.contains(option)
.click();
}

View file

@ -23,7 +23,7 @@ export default class WebAuthnPolicies {
isPasswordLess ? prop.replace("Policy", "PolicyPasswordless") : prop
}`,
).click();
cy.get(".pf-v5-c-select__menu").contains(data[prop]).click();
cy.get(".pf-v5-c-menu__list").contains(data[prop]).click();
}
return this;
}

View file

@ -3,9 +3,9 @@ import CommonPage from "../../../../../CommonPage";
export default class MappersTab extends CommonPage {
#addMapperBtn = "#mapperAction";
#fromPredefinedMappersBtn =
'ul[aria-labelledby="mapperAction"] > li:nth-child(1) a';
'ul[class="pf-v5-c-menu__list"] > li:nth-child(1) button';
#byConfigurationBtn =
'ul[aria-labelledby="mapperAction"] > li:nth-child(2) a';
'ul[class="pf-v5-c-menu__list"] > li:nth-child(2) button';
#mapperConfigurationList =
'ul[aria-label="Add predefined mappers"] > li:not([id=header])';

View file

@ -32,7 +32,7 @@ export default class CreateClientPage extends CommonPage {
#adminUrlInput = "adminUrl";
#loginThemeDrpDwn = "#login_theme";
#loginThemeList = 'ul[class="pf-v5-c-select__menu"]';
#loginThemeList = 'ul[class="pf-v5-c-menu__list"]';
#consentRequiredSwitch = '[for="consentRequired"] .pf-v5-c-switch__toggle';
#consentRequiredSwitchInput = "#consentRequired";
#displayClientOnScreenSwitch =

View file

@ -85,9 +85,7 @@ export default class AdvancedTab extends PageObject {
selectAccessTokenSignatureAlgorithm(algorithm: string) {
cy.get(this.#accessTokenSignatureAlgorithmInput).click();
cy.get(this.#accessTokenSignatureAlgorithmInput + " + ul")
.contains(algorithm)
.click();
cy.get(".pf-v5-c-menu__list").contains(algorithm).click();
return this;
}
@ -200,7 +198,9 @@ export default class AdvancedTab extends PageObject {
selectKeyForCodeExchangeInput(input: string) {
cy.get(this.#keyForCodeExchangeInput).click();
cy.get(this.#keyForCodeExchangeInput + " + ul")
cy.get(this.#keyForCodeExchangeInput)
.parent()
.get("ul")
.contains(input)
.click();
return this;
@ -228,17 +228,13 @@ export default class AdvancedTab extends PageObject {
selectBrowserFlowInput(input: string) {
cy.get(this.#browserFlowInput).click();
cy.get(this.#browserFlowInput + " + ul")
.contains(input)
.click();
cy.get(this.#browserFlowInput).parent().get("ul").contains(input).click();
return this;
}
selectDirectGrantInput(input: string) {
cy.get(this.#directGrantInput).click();
cy.get(this.#directGrantInput + " + ul")
.contains(input)
.click();
cy.get(this.#directGrantInput).parent().get("ul").contains(input).click();
return this;
}

View file

@ -4,7 +4,7 @@ import Masthead from "../../Masthead";
const masthead = new Masthead();
export enum LoginFlowOption {
empty = "",
empty = "First login flow override",
none = "None",
browser = "browser",
directGrant = "direct grant",
@ -171,7 +171,7 @@ export default class ProviderBaseGeneralSettingsPage extends PageObject {
cy.get(this.#firstLoginFlowSelect).click();
super.clickSelectMenuItem(
loginFlowOption,
cy.get(".pf-v5-c-select__menu-item").contains(loginFlowOption),
cy.get(".pf-v5-c-menu__list-item").contains(loginFlowOption),
);
return this;
}
@ -180,7 +180,7 @@ export default class ProviderBaseGeneralSettingsPage extends PageObject {
cy.get(this.#postLoginFlowSelect).click();
super.clickSelectMenuItem(
loginFlowOption,
cy.get(".pf-v5-c-select__menu-item").contains(loginFlowOption),
cy.get(".pf-v5-c-menu__list-item").contains(loginFlowOption),
);
return this;
}
@ -191,7 +191,7 @@ export default class ProviderBaseGeneralSettingsPage extends PageObject {
cy.get(this.#clientAssertionSigningAlg).click();
super.clickSelectMenuItem(
clientAssertionSigningAlg,
cy.get(".pf-v5-c-select__menu-item").contains(clientAssertionSigningAlg),
cy.get(".pf-v5-c-menu__list-item").contains(clientAssertionSigningAlg),
);
return this;
}
@ -205,7 +205,7 @@ export default class ProviderBaseGeneralSettingsPage extends PageObject {
cy.get(this.#syncModeSelect).click();
super.clickSelectMenuItem(
syncModeOption,
cy.get(".pf-v5-c-select__menu-item").contains(syncModeOption),
cy.get(".pf-v5-c-menu__list-item").contains(syncModeOption),
);
return this;
}
@ -214,7 +214,7 @@ export default class ProviderBaseGeneralSettingsPage extends PageObject {
cy.get(this.#promptSelect).click();
super.clickSelectMenuItem(
promptOption,
cy.get(".pf-v5-c-select__menu-item").contains(promptOption).parent(),
cy.get(".pf-v5-c-menu__list-item").contains(promptOption).parent(),
);
return this;
}
@ -393,7 +393,7 @@ export default class ProviderBaseGeneralSettingsPage extends PageObject {
cy.findByTestId("jump-link-openid-connect-settings").click();
cy.get(this.#clientAuth)
.click()
.get(".pf-v5-c-select__menu-item")
.get(".pf-v5-c-menu__list-item")
.contains(option)
.click();
return this;
@ -403,7 +403,7 @@ export default class ProviderBaseGeneralSettingsPage extends PageObject {
cy.findByTestId("jump-link-openid-connect-settings").click();
cy.get(this.#clientAssertionSigningAlg)
.click()
.get(".pf-v5-c-select__menu-item")
.get(".pf-v5-c-menu__list-item")
.contains(alg)
.click();
return this;
@ -413,25 +413,25 @@ export default class ProviderBaseGeneralSettingsPage extends PageObject {
cy.findByTestId("jump-link-openid-connect-settings").click();
cy.get(this.#clientAuth)
.click()
.get(".pf-v5-c-select__menu-item")
.get(".pf-v5-c-menu__list-item")
.contains(ClientAuthentication.post)
.click();
cy.get(this.#jwtX509HeadersSwitch).should("not.exist");
cy.get(this.#clientAuth)
.click()
.get(".pf-v5-c-select__menu-item")
.get(".pf-v5-c-menu__list-item")
.contains(ClientAuthentication.basicAuth)
.click();
cy.get(this.#jwtX509HeadersSwitch).should("not.exist");
cy.get(this.#clientAuth)
.click()
.get(".pf-v5-c-select__menu-item")
.get(".pf-v5-c-menu__list-item")
.contains(ClientAuthentication.jwt)
.click();
cy.get(this.#jwtX509HeadersSwitch).should("not.exist");
cy.get(this.#clientAuth)
.click()
.get(".pf-v5-c-select__menu-item")
.get(".pf-v5-c-menu__list-item")
.contains(ClientAuthentication.jwtPrivKey)
.click();
cy.get(this.#jwtX509HeadersSwitch).should("exist");

View file

@ -8,25 +8,20 @@ export default class ProviderPage {
// LdapSettingsGeneral input values
#ldapNameInput = "name";
#ldapVendorInput = "#kc-vendor";
#ldapVendorList = "#kc-vendor + ul";
// LdapSettingsConnection input values
connectionUrlInput = "config.connectionUrl.0";
truststoreSpiInput = "#kc-use-truststore-spi";
truststoreSpiList = "#kc-use-truststore-spi + ul";
truststoreSpiInput = "#useTruststoreSpi";
connectionTimeoutInput = "config.connectionTimeout.0";
bindTypeInput = "#kc-bind-type";
#bindTypeList = "#kc-bind-type + ul";
bindDnInput = "config.bindDn.0";
bindCredsInput = "config.bindCredential.0";
#testConnectionBtn = "test-connection-button";
#testAuthBtn = "test-auth-button";
// LdapSettingsSearching input values
ldapEditModeInput = "#kc-edit-mode";
#ldapEditModeList = "#kc-edit-mode + ul";
ldapEditModeInput = "#editMode";
ldapSearchScopeInput = "#kc-search-scope";
#ldapSearchScopeInputList = "#kc-search-scope + ul";
ldapPagination = "ui-pagination";
ldapUsersDnInput = "config.usersDn.0";
ldapUserLdapAttInput = "config.usernameLDAPAttribute.0";
@ -54,13 +49,9 @@ export default class ProviderPage {
// SettingsCache input values
#cacheDayInput = "#kc-eviction-day";
#cacheDayList = "#kc-eviction-day + ul";
#cacheHourInput = "#kc-eviction-hour";
#cacheHourList = "#kc-eviction-hour + ul";
#cacheMinuteInput = "#kc-eviction-minute";
#cacheMinuteList = "#kc-eviction-minute + ul";
#cachePolicyInput = "#kc-cache-policy";
#cachePolicyList = "#kc-cache-policy + ul";
// Mapper input values
#userModelAttInput = "config.user🍺model🍺attribute";
@ -119,15 +110,19 @@ export default class ProviderPage {
switch (unit) {
case "day":
cy.get(this.#cacheDayInput).click();
cy.get(this.#cacheDayList).contains(time).click();
cy.get(this.#cacheDayInput).parent().find("ul").contains(time).click();
break;
case "hour":
cy.get(this.#cacheHourInput).click();
cy.get(this.#cacheHourList).contains(time).click();
cy.get(this.#cacheHourInput).parent().find("ul").contains(time).click();
break;
case "minute":
cy.get(this.#cacheMinuteInput).click();
cy.get(this.#cacheMinuteList).contains(time).click();
cy.get(this.#cacheMinuteInput)
.parent()
.find("ul")
.contains(time)
.click();
break;
default:
console.log("Invalid cache time, must be 'day', 'hour', or 'minute'.");
@ -187,7 +182,7 @@ export default class ProviderPage {
fillSelect(selectField: string, value: string) {
cy.get(selectField).click();
cy.get(`${selectField} + ul`).contains(value).click();
cy.get(selectField).parent().find("ul").contains(value).click();
}
fillTextField(textField: string, value: string) {
@ -218,7 +213,11 @@ export default class ProviderPage {
cy.findByTestId(this.#ldapNameInput).clear().type(name);
if (vendor) {
cy.get(this.#ldapVendorInput).click();
cy.get(this.#ldapVendorList).contains(vendor).click();
cy.get(this.#ldapVendorInput)
.parent()
.find("ul")
.contains(vendor)
.click();
}
return this;
}
@ -234,11 +233,15 @@ export default class ProviderPage {
cy.findByTestId(this.connectionUrlInput).clear().type(connectionUrl);
cy.get(this.bindTypeInput).click();
cy.get(this.#bindTypeList).contains(bindType).click();
cy.get(this.bindTypeInput).parent().find("ul").contains(bindType).click();
if (truststoreSpi) {
cy.get(this.truststoreSpiInput).click();
cy.get(this.truststoreSpiList).contains(truststoreSpi).click();
cy.get(this.truststoreSpiInput)
.parent()
.find("ul")
.contains(truststoreSpi)
.click();
}
if (connectionTimeout) {
cy.findByTestId(this.connectionTimeoutInput)
@ -266,7 +269,11 @@ export default class ProviderPage {
readTimeout?: string,
) {
cy.get(this.ldapEditModeInput).click();
cy.get(this.#ldapEditModeList).contains(editMode).click();
cy.get(this.ldapEditModeInput)
.parent()
.find("ul")
.contains(editMode)
.click();
cy.findByTestId(this.ldapUsersDnInput).clear().type(usersDn);
if (userLdapAtt) {
cy.findByTestId(this.ldapUserLdapAttInput).clear().type(userLdapAtt);
@ -287,7 +294,11 @@ export default class ProviderPage {
}
if (searchScope) {
cy.get(this.ldapSearchScopeInput).click();
cy.get(this.#ldapSearchScopeInputList).contains(searchScope).click();
cy.get(this.ldapSearchScopeInput)
.parent()
.find("ul")
.contains(searchScope)
.click();
}
if (readTimeout) {
cy.findByTestId(this.ldapReadTimeout).clear().type(readTimeout);
@ -297,7 +308,11 @@ export default class ProviderPage {
selectCacheType(cacheType: string) {
cy.get(this.#cachePolicyInput).click();
cy.get(this.#cachePolicyList).contains(cacheType).click();
cy.get(this.#cachePolicyInput)
.parent()
.find("ul")
.contains(cacheType)
.click();
return this;
}

View file

@ -31,6 +31,7 @@ class CreateRealmRolePage {
clickActionMenu(item: string) {
cy.findByTestId("action-dropdown")
.click()
.parent()
.within(() => {
cy.findByText(item).click();
});

View file

@ -23,49 +23,45 @@ export default class RealmSettingsPage extends CommonPage {
userProfileTab = "rs-user-profile-tab";
tokensTab = "rs-tokens-tab";
selectLoginTheme = "#kc-login-theme";
loginThemeList = "#kc-login-theme + ul";
loginThemeList = "[data-testid='select-login-theme']";
selectAccountTheme = "#kc-account-theme";
accountThemeList = "#kc-account-theme + ul";
accountThemeList = "[data-testid='select-account-theme']";
selectAdminTheme = "#kc-admin-ui-theme";
adminThemeList = "#kc-admin-ui-theme + ul";
adminThemeList = "[data-testid='select-admin-theme']";
selectEmailTheme = "#kc-email-theme";
emailThemeList = "#kc-email-theme + ul";
emailThemeList = "[data-testid='select-email-theme']";
ssoSessionIdleSelectMenu = "#kc-sso-session-idle-select-menu";
ssoSessionIdleSelectMenuList = "#kc-sso-session-idle-select-menu > div > ul";
ssoSessionIdleSelectMenuList = "#kc-sso-session-idle-select-menu ul";
ssoSessionMaxSelectMenu = "#kc-sso-session-max-select-menu";
ssoSessionMaxSelectMenuList = "#kc-sso-session-max-select-menu > div > ul";
ssoSessionMaxSelectMenuList = "#kc-sso-session-max-select-menu ul";
ssoSessionMaxRememberMeSelectMenu =
"#kc-sso-session-max-remember-me-select-menu";
ssoSessionMaxRememberMeSelectMenuList =
"#kc-sso-session-max-remember-me-select-menu > div > ul";
"#kc-sso-session-max-remember-me-select-menu ul";
ssoSessionIdleRememberMeSelectMenu =
"#kc-sso-session-idle-remember-me-select-menu";
ssoSessionIdleRememberMeSelectMenuList =
"#kc-sso-session-idle-remember-me-select-menu > div > ul";
"#kc-sso-session-idle-remember-me-select-menu ul";
clientSessionIdleSelectMenu = "#kc-client-session-idle-select-menu";
clientSessionIdleSelectMenuList =
"#kc-client-session-idle-select-menu > div > ul";
clientSessionIdleSelectMenuList = "#kc-client-session-idle-select-menu ul";
clientSessionMaxSelectMenu = "#kc-client-session-max-select-menu";
clientSessionMaxSelectMenuList =
"#kc-client-session-max-select-menu > div > ul";
clientSessionMaxSelectMenuList = "#kc-client-session-max-select-menu ul";
offlineSessionIdleSelectMenu = "#kc-offline-session-idle-select-menu";
loginTimeoutSelectMenu = "#kc-login-timeout-select-menu";
loginTimeoutSelectMenuList = "#kc-login-timeout-select-menu > div > ul";
loginTimeoutSelectMenuList = "#kc-login-timeout-select-menu ul";
loginActionTimeoutSelectMenu = "#kc-login-action-timeout-select-menu";
loginActionTimeoutSelectMenuList =
"#kc-login-action-timeout-select-menu > div > ul";
loginActionTimeoutSelectMenuList = "#kc-login-action-timeout-select-menu ul";
selectDefaultLocale = "#kc-default-locale";
defaultLocaleList = "select-default-locale";
supportedLocalesTypeahead =
"#kc-l-supported-locales-select-multi-typeahead-typeahead";
supportedLocalesTypeahead = "#supportedLocales";
supportedLocalesToggle = "#kc-l-supported-locales";
emailSaveBtn = "email-tab-save";
managedAccessSwitch = "userManagedAccessAllowed";
@ -129,29 +125,27 @@ export default class RealmSettingsPage extends CommonPage {
accessTokenLifespanSelectMenu = "#kc-access-token-lifespan-select-menu";
accessTokenLifespanSelectMenuList =
"#kc-access-token-lifespan-select-menu > div > ul";
"#kc-access-token-lifespan-select-menu ul";
parRequestUriLifespanSelectMenu = "#par-request-uri-lifespan-select-menu";
parRequestUriLifespanSelectMenuList =
"#par-request-uri-lifespan-select-menu > div > ul";
"#par-request-uri-lifespan-select-menu ul";
accessTokenLifespanImplicitSelectMenu =
"#kc-access-token-lifespan-implicit-select-menu";
accessTokenLifespanImplicitSelectMenuList =
"#kc-access-token-lifespan-implicit-select-menu > div > ul";
"#kc-access-token-lifespan-implicit-select-menu ul";
clientLoginTimeoutSelectMenu = "#kc-client-login-timeout-select-menu";
clientLoginTimeoutSelectMenuList =
"#kc-client-login-timeout-select-menu > div > ul";
clientLoginTimeoutSelectMenuList = "#kc-client-login-timeout-select-menu ul";
offlineSessionMaxSelectMenu = "#kc-offline-session-max-select-menu";
offlineSessionMaxSelectMenuList =
"#kc-offline-session-max-select-menu > div > ul";
offlineSessionMaxSelectMenuList = "#kc-offline-session-max-select-menu ul";
userInitiatedActionLifespanSelectMenu =
"#kc-user-initiated-action-lifespan-select-menu";
userInitiatedActionLifespanSelectMenuList =
"#kc-user-initiated-action-lifespan-select-menu > div > ul";
"#kc-user-initiated-action-lifespan-select-menu ul";
defaultAdminInitatedInputSelectMenu =
"#kc-default-admin-initiated-select-menu";
@ -159,18 +153,17 @@ export default class RealmSettingsPage extends CommonPage {
"#kc-default-admin-initiated-select-menu";
emailVerificationSelectMenu = "#kc-email-verification-select-menu";
emailVerificationSelectMenuList =
"#kc-email-verification-select-menu > div > ul";
emailVerificationSelectMenuList = "#kc-email-verification-select-menu ul";
idpEmailVerificationSelectMenu = "#kc-idp-email-verification-select-menu";
idpEmailVerificationSelectMenuList =
"#kc-idp-email-verification-select-menu > div > ul";
"#kc-idp-email-verification-select-menu ul";
forgotPasswordSelectMenu = "#kc-forgot-pw-select-menu";
forgotPasswordSelectMenuList = "#kc-forgot-pw-select-menu > div > ul";
forgotPasswordSelectMenuList = "#kc-forgot-pw-select-menu ul";
executeActionsSelectMenu = "#kc-execute-actions-select-menu";
executeActionsSelectMenuList = "#kc-execute-actions-select-menu > div > ul";
executeActionsSelectMenuList = "#kc-execute-actions-select-menu ul";
#formViewProfilesView = "formView-profilesView";
#jsonEditorProfilesView = "jsonEditor-profilesView";
@ -197,7 +190,7 @@ export default class RealmSettingsPage extends CommonPage {
#jsonEditorSavePoliciesBtn = "jsonEditor-policies-saveBtn";
#jsonEditorReloadBtn = "jsonEditor-reloadBtn";
#jsonEditor = ".monaco-scrollable-element.editor-scrollable.vs";
#clientPolicyDrpDwn = '[data-testid="action-dropdown"] button';
#clientPolicyDrpDwn = '[data-testid="action-dropdown"]';
#deleteclientPolicyDrpDwn = "deleteClientPolicyDropdown";
#clientProfileOne =
'a[href*="realm-settings/client-policies/Test/edit-profile"]';
@ -206,8 +199,8 @@ export default class RealmSettingsPage extends CommonPage {
#clientPolicy = 'a[href*="realm-settings/client-policies/Test/edit-policy"]';
#reloadBtn = "reloadProfile";
#addExecutor = "addExecutor";
#addExecutorDrpDwn = ".pf-v5-c-select__toggle";
#addExecutorDrpDwnOption = "executorType-select";
#addExecutorDrpDwn = "#kc-executor";
#addExecutorDrpDwnOption = ".pf-v5-c-menu__list";
#addExecutorCancelBtn = ".pf-v5-c-form__actions a";
#addExecutorSaveBtn = "addExecutor-saveBtn";
#availablePeriodExecutorFld = "available-period";
@ -217,22 +210,21 @@ export default class RealmSettingsPage extends CommonPage {
#listingPage = new ListingPage();
#addCondition = "addCondition";
#addConditionDrpDwn = ".pf-v5-c-select__toggle";
#addConditionDrpDwnOption = "conditionType-select";
#addConditionDrpDwn = "#provider";
#addConditionDrpDwnOption = ".pf-v5-c-menu__list";
#addConditionCancelBtn = "addCondition-cancelBtn";
#addConditionSaveBtn = "addCondition-saveBtn";
#clientRolesConditionLink = "client-roles-condition-link";
#clientScopesConditionLink = "client-scopes-condition-link";
#eventListenersFormLabel = ".pf-v5-c-form__label-text";
#eventListenersDrpDwn = ".pf-v5-c-select.kc_eventListeners_select";
#eventListenersDrpDwn = "#eventsListeners";
#eventListenersSaveBtn = "saveEventListenerBtn";
#eventListenersRevertBtn = "revertEventListenerBtn";
#eventListenersInputFld =
".pf-v5-c-form-control.pf-v5-c-select__toggle-typeahead";
#eventListenersDrpDwnOption = ".pf-v5-c-select__menu";
#eventListenersDrwDwnSelect =
".pf-v5-c-button.pf-v5-c-select__toggle-button.pf-m-plain";
#eventListenerRemove = '[data-ouia-component-id="Remove"]';
"#eventsListeners .pf-v5-c-text-input-group__text-input";
#eventListenersDrpDwnOption = ".pf-v5-c-menu__list";
#eventListenersDrwDwnSelect = "#eventsListeners .pf-v5-c-menu-toggle__button";
#eventListenerRemove = ".pf-v5-c-chip__actions";
#roleSelect = "config.roles0";
#selectScopeButton = "addValue";
#deleteClientRolesConditionBtn = "delete-client-roles-condition";
@ -753,7 +745,8 @@ export default class RealmSettingsPage extends CommonPage {
shouldRemoveEventFromEventListener() {
cy.get(this.#eventListenerRemove).last().click({ force: true });
cy.findByTestId(this.#eventListenersSaveBtn).click({ force: true });
cy.get(this.#eventListenersInputFld).click();
cy.findByTestId(this.#eventListenersSaveBtn).click();
cy.get(this.#alertMessage).should(
"be.visible",
"Event listener has been updated.",
@ -762,7 +755,8 @@ export default class RealmSettingsPage extends CommonPage {
}
shouldRemoveAllEventListeners() {
cy.get(".pf-v5-c-button.pf-m-plain.pf-v5-c-select__toggle-clear").click();
cy.get(".pf-v5-c-chip__actions").first().click();
cy.get(".pf-v5-c-chip__actions").click();
cy.findByTestId(this.#eventListenersSaveBtn).click();
cy.get(this.#eventListenersDrpDwn).should("not.have.text", "jboss-logging");
cy.get(this.#eventListenersDrpDwn).should("not.have.text", "email");
@ -908,7 +902,7 @@ export default class RealmSettingsPage extends CommonPage {
cy.get(this.#clientProfileTwo).click();
cy.findByTestId(this.#addExecutor).click();
cy.get(this.#addExecutorDrpDwn).click();
cy.findByTestId(this.#addExecutorDrpDwnOption)
cy.get(this.#addExecutorDrpDwnOption)
.contains("secure-ciba-signed-authn-req")
.click();
cy.get(this.#addExecutorCancelBtn).click();
@ -922,7 +916,7 @@ export default class RealmSettingsPage extends CommonPage {
cy.get(this.#clientProfileTwo).click();
cy.findByTestId(this.#addExecutor).click();
cy.get(this.#addExecutorDrpDwn).click();
cy.findByTestId(this.#addExecutorDrpDwnOption)
cy.get(this.#addExecutorDrpDwnOption)
.contains("secure-ciba-signed-authn-req")
.click();
cy.findByTestId(this.#addExecutorSaveBtn).click();
@ -1136,9 +1130,7 @@ export default class RealmSettingsPage extends CommonPage {
cy.get(this.#clientPolicy).click();
cy.findByTestId(this.#addCondition).click();
cy.get(this.#addConditionDrpDwn).click();
cy.findByTestId(this.#addConditionDrpDwnOption)
.contains("any-client")
.click();
cy.get(this.#addConditionDrpDwnOption).contains("any-client").click();
cy.findByTestId(this.#addConditionCancelBtn).click();
cy.get('h2[class*="kc-emptyConditions"]').should(
"have.text",
@ -1150,9 +1142,7 @@ export default class RealmSettingsPage extends CommonPage {
cy.get(this.#clientPolicy).click();
cy.findByTestId(this.#addCondition).click();
cy.get(this.#addConditionDrpDwn).click();
cy.findByTestId(this.#addConditionDrpDwnOption)
.contains("client-roles")
.click();
cy.get(this.#addConditionDrpDwnOption).contains("client-roles").click();
cy.findByTestId(this.#roleSelect).clear().type("manage-realm");
cy.findByTestId(this.#addConditionSaveBtn).click();
@ -1178,9 +1168,7 @@ export default class RealmSettingsPage extends CommonPage {
cy.get(this.#clientPolicy).click();
cy.findByTestId(this.#addCondition).click();
cy.get(this.#addConditionDrpDwn).click();
cy.findByTestId(this.#addConditionDrpDwnOption)
.contains("client-scopes")
.click();
cy.get(this.#addConditionDrpDwnOption).contains("client-scopes").click();
this.addClientScopes();
@ -1293,7 +1281,9 @@ export default class RealmSettingsPage extends CommonPage {
deleteClientPolicyFromDetails() {
cy.get(this.#clientPolicyDrpDwn).click({ force: true });
cy.findByTestId(this.#deleteclientPolicyDrpDwn).click({ force: true });
cy.findByTestId(this.#deleteclientPolicyDrpDwn)
.find("button")
.click({ force: true });
return this;
}

View file

@ -142,8 +142,12 @@ export default class UserProfile {
}
setAttributeGroup(group: string) {
cy.get("#kc-attributeGroup").click();
cy.get("button.pf-v5-c-select__menu-item").contains(group).click();
cy.get("#group").click();
cy.get("#group")
.parent()
.get(".pf-v5-c-menu__list-item")
.contains(group)
.click();
return this;
}

View file

@ -57,7 +57,7 @@ export default class CredentialsPage {
}
clickResetModalAction(index: number) {
cy.get("[data-testid=credential-reset-modal] .pf-v5-c-select__menu")
cy.get("[data-testid=credential-reset-modal] .pf-v5-c-menu__list")
.contains(this.#resetActions[index])
.click();

View file

@ -9,8 +9,8 @@ export default class ModalUtils extends PageObject {
#cancelModalBtn = "cancel";
#closeModalBtn = ".pf-v5-c-modal-box .pf-m-plain";
#copyToClipboardBtn = '[id*="copy-button"]';
#addModalDropdownBtn = "#add-dropdown > button";
#addModalDropdownItem = "#add-dropdown [role='menuitem']";
#addModalDropdownBtn = "#add-dropdown";
#addModalDropdownItem = ".pf-v5-c-modal-box__footer .pf-v5-c-menu__content";
#addBtn = "add";
#tablePage = new TablePage(TablePage.tableSelector);

View file

@ -6,7 +6,6 @@ import {
Form,
Modal,
} from "@patternfly/react-core";
import { SelectVariant } from "@patternfly/react-core/deprecated";
import { FormProvider, useForm } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { SelectControl } from "@keycloak/keycloak-ui-shared";
@ -83,7 +82,6 @@ export const BindFlowDialog = ({ flowAlias, onClose }: BindFlowDialogProps) => {
value: t(`flow.${REALM_FLOWS.get(key)}`),
}))}
controller={{ defaultValue: flowKeys[0] }}
variant={SelectVariant.single}
menuAppendTo="parent"
aria-label={t("chooseBindingType")}
/>

View file

@ -7,6 +7,7 @@ import {
ButtonVariant,
DataList,
DragDrop,
DropdownItem,
Droppable,
Label,
PageSection,
@ -16,7 +17,6 @@ import {
ToolbarContent,
ToolbarItem,
} from "@patternfly/react-core";
import { DropdownItem } from "@patternfly/react-core/deprecated";
import { DomainIcon, TableIcon } from "@patternfly/react-icons";
import { useState } from "react";
import { Trans, useTranslation } from "react-i18next";

View file

@ -1,10 +1,6 @@
import type { AuthenticationProviderRepresentation } from "@keycloak/keycloak-admin-client/lib/defs/authenticatorConfigRepresentation";
import { Tooltip } from "@patternfly/react-core";
import {
Dropdown,
DropdownItem,
DropdownToggle,
} from "@patternfly/react-core/deprecated";
import { DropdownList, Tooltip } from "@patternfly/react-core";
import { Dropdown, DropdownItem, MenuToggle } from "@patternfly/react-core";
import { PlusIcon } from "@patternfly/react-icons";
import { useState } from "react";
import { useTranslation } from "react-i18next";
@ -49,19 +45,24 @@ export const AddFlowDropdown = ({
<Tooltip content={t("add")}>
<>
<Dropdown
isPlain
position="right"
data-testid={`${execution.displayName}-edit-dropdown`}
popperProps={{
position: "right",
}}
isOpen={open}
toggle={
<DropdownToggle
onToggle={(_event, val) => setOpen(val)}
toggle={(ref) => (
<MenuToggle
ref={ref}
variant="plain"
onClick={() => setOpen(!open)}
aria-label={t("add")}
data-testid={`${execution.displayName}-edit-dropdown`}
>
<PlusIcon />
</DropdownToggle>
}
dropdownItems={[
</MenuToggle>
)}
onSelect={() => setOpen(false)}
>
<DropdownList>
<DropdownItem
key="addStep"
onClick={() =>
@ -69,19 +70,18 @@ export const AddFlowDropdown = ({
}
>
{t("addStep")}
</DropdownItem>,
</DropdownItem>
<DropdownItem
key="addCondition"
onClick={() => setType("condition")}
>
{t("addCondition")}
</DropdownItem>,
</DropdownItem>
<DropdownItem key="addSubFlow" onClick={() => setType("subFlow")}>
{t("addSubFlow")}
</DropdownItem>,
]}
onSelect={() => setOpen(false)}
/>
</DropdownItem>
</DropdownList>
</Dropdown>
{type && type !== "subFlow" && (
<AddStepModal
name={execution.displayName!}

View file

@ -1,10 +1,11 @@
import {
MenuToggle,
Select,
SelectList,
SelectOption,
} from "@patternfly/react-core";
import { useState } from "react";
import { useTranslation } from "react-i18next";
import {
Select,
SelectOption,
SelectVariant,
} from "@patternfly/react-core/deprecated";
import type { ExpandableExecution } from "../execution-model";
@ -30,18 +31,26 @@ export const FlowRequirementDropdown = ({
<>
{flow.requirementChoices && flow.requirementChoices.length > 1 && (
<Select
className="keycloak__authentication__requirement-dropdown"
variant={SelectVariant.single}
onToggle={(_event, val) => setOpen(val)}
onOpenChange={(isOpen) => setOpen(isOpen)}
onSelect={(_event, value) => {
flow.requirement = value.toString();
flow.requirement = value?.toString();
onChange(flow);
setOpen(false);
}}
selections={[flow.requirement]}
selected={flow.requirement}
isOpen={open}
toggle={(ref) => (
<MenuToggle
className="keycloak__authentication__requirement-dropdown"
ref={ref}
onClick={() => setOpen(!open)}
isExpanded={open}
>
{t(`requirements.${flow.requirement}`)}
</MenuToggle>
)}
>
{options}
<SelectList>{options}</SelectList>
</Select>
)}
{(!flow.requirementChoices || flow.requirementChoices.length <= 1) && (

View file

@ -17,9 +17,9 @@ import {
ToolbarContent,
ToolbarItem,
Select,
SelectOption,
MenuToggle,
SelectList,
SelectOption,
} from "@patternfly/react-core";
import { PlusCircleIcon } from "@patternfly/react-icons";
import { useEffect, useMemo, useState } from "react";
@ -53,9 +53,6 @@ const PolicySelect = ({ onSelect, selectedPolicies }: PolicySelectProps) => {
return (
<Select
style={{
width: "300px",
}}
onSelect={(_, selection) => {
onSelect(selection as PasswordPolicyTypeRepresentation);
setOpen(false);
@ -66,6 +63,7 @@ const PolicySelect = ({ onSelect, selectedPolicies }: PolicySelectProps) => {
onClick={() => setOpen(!open)}
isExpanded={open}
isDisabled={policies?.length === 0}
style={{ width: "300px" }}
data-testid="add-policy"
>
{t("addPolicy")}

View file

@ -10,7 +10,6 @@ import {
Text,
TextContent,
} from "@patternfly/react-core";
import { SelectVariant } from "@patternfly/react-core/deprecated";
import { QuestionCircleIcon } from "@patternfly/react-icons";
import { useEffect } from "react";
import { FormProvider, useForm } from "react-hook-form";
@ -84,15 +83,12 @@ const WebauthnSelect = ({
<SelectControl
name={name}
label={t(label)}
variant={
isMultiSelect ? SelectVariant.typeaheadMulti : SelectVariant.single
}
variant={isMultiSelect ? "typeaheadMulti" : "single"}
controller={{ defaultValue: options[0] }}
options={options.map((option) => ({
key: option,
value: labelPrefix ? t(`${labelPrefix}.${option}`) : option,
}))}
typeAheadAriaLabel={t(name)}
/>
);
};

View file

@ -1,5 +1,9 @@
import { AlertVariant } from "@patternfly/react-core";
import { Select } from "@patternfly/react-core/deprecated";
import {
AlertVariant,
MenuToggle,
Select,
SelectList,
} from "@patternfly/react-core";
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { useAdminClient } from "../admin-client";
@ -33,13 +37,19 @@ export const ChangeTypeDropdown = ({
return (
<Select
toggleId="change-type-dropdown"
aria-label="change-type-to"
isOpen={open}
selections={[]}
isDisabled={selectedRows.length === 0}
placeholderText={t("changeTypeTo")}
onToggle={(_event, val) => setOpen(val)}
toggle={(ref) => (
<MenuToggle
id="change-type-dropdown"
isDisabled={selectedRows.length === 0}
ref={ref}
onClick={() => setOpen(!open)}
isExpanded={open}
>
{t("changeTypeTo")}
</MenuToggle>
)}
onSelect={async (_, value) => {
try {
await Promise.all(
@ -63,10 +73,12 @@ export const ChangeTypeDropdown = ({
}
}}
>
{clientScopeTypesSelectOptions(
t,
!clientId ? allClientScopeTypes : undefined,
)}
<SelectList>
{clientScopeTypesSelectOptions(
t,
!clientId ? allClientScopeTypes : undefined,
)}
</SelectList>
</Select>
);
};

View file

@ -2,14 +2,14 @@ import {
AlertVariant,
Button,
ButtonVariant,
Dropdown,
DropdownItem,
DropdownList,
MenuToggle,
PageSection,
ToolbarItem,
} from "@patternfly/react-core";
import {
Dropdown,
DropdownItem,
KebabToggle,
} from "@patternfly/react-core/deprecated";
import { EllipsisVIcon } from "@patternfly/react-icons";
import { cellWidth } from "@patternfly/react-table";
import { useState } from "react";
import { useTranslation } from "react-i18next";
@ -256,17 +256,23 @@ export default function ClientScopesSection() {
</ToolbarItem>
<ToolbarItem>
<Dropdown
toggle={
<KebabToggle
onToggle={(_event, val) => setKebabOpen(val)}
/>
}
shouldFocusToggleOnSelect
toggle={(ref) => (
<MenuToggle
data-testid="kebab"
aria-label="Kebab toggle"
ref={ref}
onClick={() => setKebabOpen(!kebabOpen)}
variant="plain"
>
<EllipsisVIcon />
</MenuToggle>
)}
isOpen={kebabOpen}
isPlain
dropdownItems={[
>
<DropdownList>
<DropdownItem
key="action"
component="button"
data-testid="delete"
isDisabled={selectedScopes.length === 0}
onClick={() => {
toggleDeleteDialog();
@ -274,9 +280,9 @@ export default function ClientScopesSection() {
}}
>
{t("delete")}
</DropdownItem>,
]}
/>
</DropdownItem>
</DropdownList>
</Dropdown>
</ToolbarItem>
</>
}

View file

@ -6,11 +6,11 @@ import {
Alert,
AlertVariant,
ButtonVariant,
DropdownItem,
PageSection,
Tab,
TabTitleText,
} from "@patternfly/react-core";
import { DropdownItem } from "@patternfly/react-core/deprecated";
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { useNavigate } from "react-router-dom";

View file

@ -2,12 +2,6 @@ import { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import type { Path } from "react-router-dom";
import { Link } from "react-router-dom";
import {
Dropdown,
DropdownItem,
DropdownToggle,
} from "@patternfly/react-core/deprecated";
import { CaretDownIcon } from "@patternfly/react-icons";
import type ClientRepresentation from "@keycloak/keycloak-admin-client/lib/defs/clientRepresentation";
import type ClientScopeRepresentation from "@keycloak/keycloak-admin-client/lib/defs/clientScopeRepresentation";
@ -21,6 +15,12 @@ import {
Action,
KeycloakDataTable,
} from "../../components/table-toolbar/KeycloakDataTable";
import {
Dropdown,
DropdownItem,
DropdownList,
MenuToggle,
} from "@patternfly/react-core";
type MapperListProps = {
model: ClientScopeRepresentation | ClientRepresentation;
@ -114,32 +114,27 @@ export const MapperList = ({
toolbarItem={
<Dropdown
onSelect={() => setMapperAction(false)}
toggle={
<DropdownToggle
toggleVariant="primary"
toggle={(ref) => (
<MenuToggle
ref={ref}
variant="primary"
id="mapperAction"
onToggle={() => setMapperAction(!mapperAction)}
toggleIndicator={CaretDownIcon}
onClick={() => setMapperAction(!mapperAction)}
>
{t("addMapper")}
</DropdownToggle>
}
</MenuToggle>
)}
isOpen={mapperAction}
dropdownItems={[
<DropdownItem
key="predefined"
onClick={() => toggleAddMapperDialog(true)}
>
>
<DropdownList>
<DropdownItem onClick={() => toggleAddMapperDialog(true)}>
{t("fromPredefinedMapper")}
</DropdownItem>,
<DropdownItem
key="byConfiguration"
onClick={() => toggleAddMapperDialog(false)}
>
</DropdownItem>
<DropdownItem onClick={() => toggleAddMapperDialog(false)}>
{t("byConfiguration")}
</DropdownItem>,
]}
/>
</DropdownItem>
</DropdownList>
</Dropdown>
}
actions={[
{

View file

@ -5,11 +5,11 @@ import {
AlertVariant,
Button,
ButtonVariant,
DropdownItem,
FormGroup,
PageSection,
TextInput,
} from "@patternfly/react-core";
import { DropdownItem } from "@patternfly/react-core/deprecated";
import { useState } from "react";
import { FormProvider, useForm } from "react-hook-form";
import { useTranslation } from "react-i18next";

View file

@ -1,6 +1,5 @@
import type ClientScopeRepresentation from "@keycloak/keycloak-admin-client/lib/defs/clientScopeRepresentation";
import { ActionGroup, Button } from "@patternfly/react-core";
import { SelectVariant } from "@patternfly/react-core/deprecated";
import { useEffect } from "react";
import { FormProvider, useForm, useWatch } from "react-hook-form";
import { useTranslation } from "react-i18next";
@ -129,11 +128,10 @@ export const ScopeForm = ({ clientScope, save }: ScopeFormProps) => {
}}
/>
<SelectControl
id="kc-type"
name="type"
label={t("type")}
toggleId="kc-type"
labelIcon={t("scopeTypeHelp")}
variant={SelectVariant.single}
controller={{ defaultValue: allClientScopeTypes[0] }}
options={allClientScopeTypes.map((key) => ({
key,
@ -142,11 +140,10 @@ export const ScopeForm = ({ clientScope, save }: ScopeFormProps) => {
/>
{!clientScope && (
<SelectControl
id="kc-protocol"
name="protocol"
label={t("protocol")}
toggleId="kc-protocol"
labelIcon={t("protocolHelp")}
variant={SelectVariant.single}
controller={{ defaultValue: providers[0] }}
options={providers.map((option) => ({
key: option,

View file

@ -1,13 +1,15 @@
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { ToolbarItem } from "@patternfly/react-core";
import {
Dropdown,
DropdownItem,
DropdownToggle,
DropdownList,
MenuToggle,
Select,
SelectList,
SelectOption,
} from "@patternfly/react-core/deprecated";
ToolbarItem,
} from "@patternfly/react-core";
import { FilterIcon } from "@patternfly/react-icons";
import {
@ -70,18 +72,20 @@ export const SearchDropdown = ({
return (
<Dropdown
className="keycloak__client-scopes__searchtype"
toggle={
<DropdownToggle
toggle={(ref) => (
<MenuToggle
data-testid="clientScopeSearch"
ref={ref}
id="toggle-id"
onToggle={(_event, val) => setSearchToggle(val)}
onClick={() => setSearchToggle(!searchToggle)}
>
<FilterIcon /> {t(`clientScopeSearch.${searchType}`)}
</DropdownToggle>
}
</MenuToggle>
)}
isOpen={searchToggle}
dropdownItems={options}
/>
>
<DropdownList>{options}</DropdownList>
</Dropdown>
);
};
@ -109,23 +113,36 @@ export const SearchToolbar = ({
</ToolbarItem>
<ToolbarItem>
<Select
className="keycloak__client-scopes__searchtype"
onToggle={(_event, val) => setOpen(val)}
toggle={(ref) => (
<MenuToggle
data-testid="clientScopeSearchType"
ref={ref}
isExpanded={open}
onClick={() => setOpen(!open)}
>
{type === AllClientScopes.none
? t("allTypes")
: t(`clientScopeTypes.${type}`)}
</MenuToggle>
)}
onOpenChange={(val) => setOpen(val)}
isOpen={open}
selections={[
selected={
type === AllClientScopes.none
? t("allTypes")
: t(`clientScopeTypes.${type}`),
]}
: t(`clientScopeTypes.${type}`)
}
onSelect={(_, value) => {
onType(value as AllClientScopes);
setOpen(false);
}}
>
<SelectOption value={AllClientScopes.none}>
{t("allTypes")}
</SelectOption>
<>{clientScopeTypesSelectOptions(t)}</>
<SelectList>
<SelectOption value={AllClientScopes.none}>
{t("allTypes")}
</SelectOption>
{clientScopeTypesSelectOptions(t)}
</SelectList>
</Select>
</ToolbarItem>
</>
@ -141,20 +158,31 @@ export const SearchToolbar = ({
</ToolbarItem>
<ToolbarItem>
<Select
className="keycloak__client-scopes__searchtype"
onToggle={(_event, val) => setOpen(val)}
toggle={(ref) => (
<MenuToggle
data-testid="clientScopeSearchProtocol"
ref={ref}
isExpanded={open}
onClick={() => setOpen(!open)}
>
{t(`protocolTypes.${protocol}`)}
</MenuToggle>
)}
onOpenChange={(val) => setOpen(val)}
isOpen={open}
selections={[t(`protocolTypes.${protocol}`)]}
selected={t(`protocolTypes.${protocol}`)}
onSelect={(_, value) => {
onProtocol?.(value as ProtocolType);
setOpen(false);
}}
>
{PROTOCOLS.map((type) => (
<SelectOption key={type} value={type}>
{t(`protocolTypes.${type}`)}
</SelectOption>
))}
<SelectList>
{PROTOCOLS.map((type) => (
<SelectOption key={type} value={type}>
{t(`protocolTypes.${type}`)}
</SelectOption>
))}
</SelectList>
</Select>
</ToolbarItem>
</>

View file

@ -3,13 +3,13 @@ import {
AlertVariant,
ButtonVariant,
Divider,
DropdownItem,
Label,
PageSection,
Tab,
TabTitleText,
Tooltip,
} from "@patternfly/react-core";
import { DropdownItem } from "@patternfly/react-core/deprecated";
import { InfoCircleIcon } from "@patternfly/react-icons";
import { cloneDeep, sortBy } from "lodash-es";
import { useMemo, useState } from "react";

View file

@ -1,10 +1,13 @@
import { HelpItem } from "@keycloak/keycloak-ui-shared";
import { ActionGroup, Button, FormGroup } from "@patternfly/react-core";
import {
ActionGroup,
Button,
FormGroup,
MenuToggle,
Select,
SelectList,
SelectOption,
SelectVariant,
} from "@patternfly/react-core/deprecated";
} from "@patternfly/react-core";
import { useState } from "react";
import { Controller, useFormContext } from "react-hook-form";
import { useTranslation } from "react-i18next";
@ -145,21 +148,30 @@ export const AdvancedSettings = ({
control={control}
render={({ field }) => (
<Select
toggleId="keyForCodeExchange"
variant={SelectVariant.single}
onToggle={(_event, val) => setOpen(val)}
toggle={(ref) => (
<MenuToggle
id="keyForCodeExchange"
ref={ref}
onClick={() => setOpen(!open)}
isExpanded={open}
>
{[field.value || t("choose")]}
</MenuToggle>
)}
isOpen={open}
onSelect={(_, value) => {
field.onChange(value);
setOpen(false);
}}
selections={[field.value || t("choose")]}
selected={field.value}
>
{["", "S256", "plain"].map((v) => (
<SelectOption key={v} value={v}>
{v || t("choose")}
</SelectOption>
))}
<SelectList>
{["", "S256", "plain"].map((v) => (
<SelectOption key={v} value={v}>
{v || t("choose")}
</SelectOption>
))}
</SelectList>
</Select>
)}
/>

View file

@ -1,9 +1,12 @@
import { FormGroup, Split, SplitItem } from "@patternfly/react-core";
import {
FormGroup,
MenuToggle,
Select,
SelectList,
SelectOption,
SelectVariant,
} from "@patternfly/react-core/deprecated";
Split,
SplitItem,
} from "@patternfly/react-core";
import { useState } from "react";
import { Controller, useFormContext } from "react-hook-form";
import { useTranslation } from "react-i18next";
@ -57,19 +60,26 @@ export const TokenLifespan = ({
<Split hasGutter>
<SplitItem>
<Select
variant={SelectVariant.single}
onToggle={(_event, val) => setOpen(val)}
toggle={(ref) => (
<MenuToggle
ref={ref}
onClick={() => setOpen(!open)}
isExpanded={open}
>
{isExpireSet(field.value) ? t(expires) : t(inherited)}
</MenuToggle>
)}
isOpen={open}
onSelect={(_, value) => {
field.onChange(value);
setOpen(false);
}}
selections={[
isExpireSet(field.value) ? t(expires) : t(inherited),
]}
selected={isExpireSet(field.value) ? t(expires) : t(inherited)}
>
<SelectOption value="">{t(inherited)}</SelectOption>
<SelectOption value={60}>{t(expires)}</SelectOption>
<SelectList>
<SelectOption value="">{t(inherited)}</SelectOption>
<SelectOption value={60}>{t(expires)}</SelectOption>
</SelectList>
</Select>
</SplitItem>
<SplitItem hidden={!isExpireSet(field.value)}>

View file

@ -5,6 +5,11 @@ import type ResourceEvaluation from "@keycloak/keycloak-admin-client/lib/defs/re
import type ResourceRepresentation from "@keycloak/keycloak-admin-client/lib/defs/resourceRepresentation";
import type RoleRepresentation from "@keycloak/keycloak-admin-client/lib/defs/roleRepresentation";
import type ScopeRepresentation from "@keycloak/keycloak-admin-client/lib/defs/scopeRepresentation";
import {
HelpItem,
SelectControl,
TextControl,
} from "@keycloak/keycloak-ui-shared";
import {
ActionGroup,
Button,
@ -17,19 +22,9 @@ import {
Switch,
Title,
} from "@patternfly/react-core";
import {
Select,
SelectOption,
SelectVariant,
} from "@patternfly/react-core/deprecated";
import { useState } from "react";
import { Controller, FormProvider, useForm, useWatch } from "react-hook-form";
import { FormProvider, useForm, useWatch } from "react-hook-form";
import { useTranslation } from "react-i18next";
import {
FormErrorText,
HelpItem,
TextControl,
} from "@keycloak/keycloak-ui-shared";
import { ForbiddenSection } from "../../ForbiddenSection";
import { useAdminClient } from "../../admin-client";
import { useAlerts } from "../../components/alert/Alerts";
@ -104,13 +99,11 @@ const AuthorizationEvaluateContent = ({ client }: Props) => {
control,
reset,
trigger,
formState: { isValid, errors },
formState: { isValid },
} = form;
const { t } = useTranslation();
const { addError } = useAlerts();
const realm = useRealm();
const [scopesDropdownOpen, setScopesDropdownOpen] = useState(false);
const [roleDropdownOpen, setRoleDropdownOpen] = useState(false);
const [isExpanded, setIsExpanded] = useState(false);
const [applyToResourceType, setApplyToResourceType] = useState(false);
const [resources, setResources] = useState<ResourceRepresentation[]>([]);
@ -219,64 +212,26 @@ const AuthorizationEvaluateContent = ({ client }: Props) => {
label="users"
helpText={t("selectUser")}
defaultValue={[]}
variant={SelectVariant.typeahead}
variant="typeahead"
isRequired={roles?.length === 0}
/>
<FormGroup
<SelectControl
name="roleIds"
label={t("roles")}
labelIcon={
<HelpItem helpText={t("rolesHelp")} fieldLabelId="roles" />
}
fieldId="realmRole"
isRequired={user.length === 0}
>
<Controller
name="roleIds"
control={control}
defaultValue={[]}
rules={{
validate: (value) =>
(value || "").length > 0 || user.length > 0,
}}
render={({ field }) => (
<Select
placeholderText={t("selectARole")}
variant={SelectVariant.typeaheadMulti}
toggleId="role"
onToggle={(_event, val) => setRoleDropdownOpen(val)}
selections={field.value}
onSelect={(_, v) => {
const option = v.toString();
if (field.value?.includes(option)) {
field.onChange(
field.value.filter(
(item: string) => item !== option,
),
);
} else {
field.onChange([...(field.value || []), option]);
}
setRoleDropdownOpen(false);
}}
onClear={(event) => {
event.stopPropagation();
field.onChange([]);
}}
aria-label={t("realmRole")}
isOpen={roleDropdownOpen}
>
{clientRoles.map((role) => (
<SelectOption
selected={role.name === field.value}
key={role.name}
value={role.name}
/>
))}
</Select>
)}
/>
{errors.roleIds && <FormErrorText message={t("required")} />}
</FormGroup>
labelIcon={t("rolesHelp")}
variant="typeaheadMulti"
placeholderText={t("selectARole")}
controller={{
defaultValue: [],
rules: {
required: {
value: user.length === 0,
message: t("required"),
},
},
}}
options={clientRoles.map((role) => role.name!)}
/>
</FormAccess>
</PanelMainBody>
</Panel>
@ -334,54 +289,16 @@ const AuthorizationEvaluateContent = ({ client }: Props) => {
labelIcon={t("resourceTypeHelp")}
rules={{ required: t("required") }}
/>
<FormGroup
<SelectControl
name="authScopes"
label={t("authScopes")}
labelIcon={
<HelpItem
helpText={t("scopesSelect")}
fieldLabelId="client"
/>
}
fieldId="authScopes"
>
<Controller
name="authScopes"
defaultValue={[]}
control={control}
render={({ field }) => (
<Select
toggleId="authScopes"
onToggle={(_event, val) => setScopesDropdownOpen(val)}
onSelect={(_, v) => {
const option = v.toString();
if (field.value.includes(option)) {
field.onChange(
field.value.filter(
(item: string) => item !== option,
),
);
} else {
field.onChange([...field.value, option]);
}
setScopesDropdownOpen(false);
}}
selections={field.value}
variant={SelectVariant.typeaheadMulti}
typeAheadAriaLabel={t("selectAuthScopes")}
isOpen={scopesDropdownOpen}
aria-label={t("selectAuthScopes")}
>
{scopes.map((scope) => (
<SelectOption
selected={field.value.includes(scope.name!)}
key={scope.id}
value={scope.name}
/>
))}
</Select>
)}
/>
</FormGroup>
labelIcon={t("scopesSelect")}
controller={{
defaultValue: [],
}}
variant="typeaheadMulti"
options={scopes.map((s) => s.name!)}
/>
</>
)}
<ExpandableSection

View file

@ -1,10 +1,5 @@
import type ResourceRepresentation from "@keycloak/keycloak-admin-client/lib/defs/resourceRepresentation";
import { Button, TextInput } from "@patternfly/react-core";
import {
Select,
SelectOption,
SelectVariant,
} from "@patternfly/react-core/deprecated";
import { Button, SelectOption, TextInput } from "@patternfly/react-core";
import { MinusCircleIcon, PlusCircleIcon } from "@patternfly/react-icons";
import { Table, Tbody, Td, Th, Thead, Tr } from "@patternfly/react-table";
import { camelCase } from "lodash-es";
@ -15,6 +10,10 @@ import { useTranslation } from "react-i18next";
import { defaultContextAttributes } from "../utils";
import "./key-based-attribute-input.css";
import {
KeycloakSelect,
SelectVariant,
} from "../../components/select/KeycloakSelect";
export type AttributeType = {
key?: string;
@ -99,29 +98,28 @@ const ValueInput = ({
defaultValue={[]}
control={control}
render={({ field }) => (
<Select
<KeycloakSelect
toggleId={`${attribute.id}-value`}
className="kc-attribute-value-selectable"
name={`${name}.${rowIndex}.value`}
chipGroupProps={{
numChips: 1,
expandedText: t("hide"),
collapsedText: t("showRemaining"),
}}
onToggle={(_event, open) => toggleValueSelect(rowIndex, open)}
onToggle={(open) => toggleValueSelect(rowIndex, open)}
isOpen={isValueOpenArray[rowIndex]}
variant={SelectVariant.typeahead}
typeAheadAriaLabel={t("selectOrTypeAKey")}
placeholderText={t("selectOrTypeAKey")}
selections={field.value}
onSelect={(_, v) => {
onSelect={(v) => {
field.onChange(v);
toggleValueSelect(rowIndex, false);
}}
>
{renderSelectOptionType()}
</Select>
</KeycloakSelect>
)}
/>
) : (
@ -186,17 +184,16 @@ export const KeyBasedAttributeInput = ({
defaultValue=""
control={control}
render={({ field }) => (
<Select
<KeycloakSelect
toggleId={`${name}.${rowIndex}.key`}
className="kc-attribute-key-selectable"
name={`${name}.${rowIndex}.key`}
onToggle={(_event, open) => toggleKeySelect(rowIndex, open)}
onToggle={(open) => toggleKeySelect(rowIndex, open)}
isOpen={isKeyOpenArray[rowIndex]}
variant={SelectVariant.typeahead}
typeAheadAriaLabel={t("selectOrTypeAKey")}
placeholderText={t("selectOrTypeAKey")}
selections={field.value}
onSelect={(_, v) => {
onSelect={(v) => {
field.onChange(v.toString());
toggleKeySelect(rowIndex, false);
@ -211,7 +208,7 @@ export const KeyBasedAttributeInput = ({
{attribute.name}
</SelectOption>
))}
</Select>
</KeycloakSelect>
)}
/>
</Td>

View file

@ -1,26 +1,26 @@
import type PolicyRepresentation from "@keycloak/keycloak-admin-client/lib/defs/policyRepresentation";
import { DecisionStrategy } from "@keycloak/keycloak-admin-client/lib/defs/policyRepresentation";
import {
ActionGroup,
AlertVariant,
Button,
ButtonVariant,
FormGroup,
PageSection,
Radio,
Switch,
} from "@patternfly/react-core";
import { DropdownItem, SelectVariant } from "@patternfly/react-core/deprecated";
import { useState } from "react";
import { Controller, FormProvider, useForm, useWatch } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { Link, useNavigate } from "react-router-dom";
import {
FormErrorText,
HelpItem,
TextAreaControl,
TextControl,
} from "@keycloak/keycloak-ui-shared";
import {
ActionGroup,
AlertVariant,
Button,
ButtonVariant,
DropdownItem,
FormGroup,
PageSection,
Radio,
Switch,
} from "@patternfly/react-core";
import { useState } from "react";
import { Controller, FormProvider, useForm, useWatch } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { Link, useNavigate } from "react-router-dom";
import { useAdminClient } from "../../admin-client";
import { useAlerts } from "../../components/alert/Alerts";
import { useConfirmDialog } from "../../components/confirm-dialog/ConfirmDialog";
@ -39,6 +39,7 @@ import {
} from "../routes/PermissionDetails";
import { ResourcesPolicySelect } from "./ResourcesPolicySelect";
import { ScopeSelect } from "./ScopeSelect";
import { SelectVariant } from "../../components/select/KeycloakSelect";
type FormFields = PolicyRepresentation & {
resourceType: string;

View file

@ -5,15 +5,14 @@ import {
AlertVariant,
ButtonVariant,
DescriptionList,
Divider,
Dropdown,
DropdownItem,
DropdownList,
MenuToggle,
PageSection,
ToolbarItem,
} from "@patternfly/react-core";
import {
Dropdown,
DropdownItem,
DropdownSeparator,
DropdownToggle,
} from "@patternfly/react-core/deprecated";
import {
ExpandableRowContent,
Table,
@ -209,21 +208,22 @@ export const AuthorizationPermissions = ({
</ToolbarItem>
<ToolbarItem>
<Dropdown
toggle={
<DropdownToggle
onToggle={toggleCreate}
toggle={(ref) => (
<MenuToggle
ref={ref}
onClick={toggleCreate}
isDisabled={isDisabled}
toggleVariant="primary"
variant="primary"
data-testid="permissionCreateDropdown"
>
{t("createPermission")}
</DropdownToggle>
}
</MenuToggle>
)}
isOpen={createOpen}
dropdownItems={[
>
<DropdownList>
<DropdownItem
data-testid="create-resource"
key="createResourceBasedPermission"
isDisabled={isDisabled || disabledCreate?.resources}
component="button"
onClick={() =>
@ -237,11 +237,10 @@ export const AuthorizationPermissions = ({
}
>
{t("createResourceBasedPermission")}
</DropdownItem>,
<DropdownSeparator key="separator" />,
</DropdownItem>
<Divider />
<DropdownItem
data-testid="create-scope"
key="createScopeBasedPermission"
isDisabled={isDisabled || disabledCreate?.scopes}
component="button"
onClick={() =>
@ -264,9 +263,9 @@ export const AuthorizationPermissions = ({
title={t("noScopeCreateHint")}
/>
)}
</DropdownItem>,
]}
/>
</DropdownItem>
</DropdownList>
</Dropdown>
</ToolbarItem>
</>
}

View file

@ -7,10 +7,10 @@ import {
AlertVariant,
Button,
ButtonVariant,
DropdownItem,
FormGroup,
PageSection,
} from "@patternfly/react-core";
import { DropdownItem } from "@patternfly/react-core/deprecated";
import { useState } from "react";
import { FormProvider, useForm } from "react-hook-form";
import { useTranslation } from "react-i18next";

View file

@ -5,12 +5,13 @@ import type {
Clients,
PolicyQuery,
} from "@keycloak/keycloak-admin-client/lib/resources/clients";
import { Button, ButtonVariant, Chip, ChipGroup } from "@patternfly/react-core";
import {
Select,
Button,
ButtonVariant,
Chip,
ChipGroup,
SelectOption,
SelectVariant,
} from "@patternfly/react-core/deprecated";
} from "@patternfly/react-core";
import { useState } from "react";
import {
Controller,
@ -21,6 +22,11 @@ import { useTranslation } from "react-i18next";
import { Link, useNavigate } from "react-router-dom";
import { useAdminClient } from "../../admin-client";
import { useConfirmDialog } from "../../components/confirm-dialog/ConfirmDialog";
import {
KeycloakSelect,
SelectVariant,
Variant,
} from "../../components/select/KeycloakSelect";
import { useRealm } from "../../context/realm-context/RealmContext";
import { useFetch } from "../../utils/useFetch";
import useToggle from "../../utils/useToggle";
@ -35,7 +41,7 @@ type ResourcesPolicySelectProps = {
name: Type;
clientId: string;
permissionId?: string;
variant?: SelectVariant;
variant?: Variant;
preSelected?: string;
isRequired?: boolean;
};
@ -230,11 +236,11 @@ export const ResourcesPolicySelect = ({
control={control}
rules={{ validate: (value) => !isRequired || value!.length > 0 }}
render={({ field }) => (
<Select
<KeycloakSelect
toggleId={name}
variant={variant}
onToggle={(_event, val) => setOpen(val)}
onFilter={(_, filter) => {
onToggle={(val) => setOpen(val)}
onFilter={(filter) => {
setSearch(filter);
return toSelectOptions();
}}
@ -243,7 +249,7 @@ export const ResourcesPolicySelect = ({
setSearch("");
}}
selections={field.value}
onSelect={(_, selectedValue) => {
onSelect={(selectedValue) => {
const option = selectedValue.toString();
if (variant === SelectVariant.typeaheadMulti) {
const changedValue = field.value?.find(
@ -287,7 +293,7 @@ export const ResourcesPolicySelect = ({
}
>
{toSelectOptions()}
</Select>
</KeycloakSelect>
)}
/>
</>

View file

@ -4,9 +4,9 @@ import {
AlertVariant,
Button,
ButtonVariant,
DropdownItem,
PageSection,
} from "@patternfly/react-core";
import { DropdownItem } from "@patternfly/react-core/deprecated";
import { useState } from "react";
import { FormProvider, useForm } from "react-hook-form";
import { useTranslation } from "react-i18next";

View file

@ -1,15 +1,15 @@
import type ScopeRepresentation from "@keycloak/keycloak-admin-client/lib/defs/scopeRepresentation";
import { FormGroup } from "@patternfly/react-core";
import {
Select,
SelectOption,
SelectVariant,
} from "@patternfly/react-core/deprecated";
import { HelpItem } from "@keycloak/keycloak-ui-shared";
import { FormGroup, SelectOption } from "@patternfly/react-core";
import { useState } from "react";
import { Controller, useFormContext } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { HelpItem } from "@keycloak/keycloak-ui-shared";
import { useAdminClient } from "../../admin-client";
import { KeycloakSpinner } from "../../components/keycloak-spinner/KeycloakSpinner";
import {
KeycloakSelect,
SelectVariant,
} from "../../components/select/KeycloakSelect";
import { useFetch } from "../../utils/useFetch";
type Scope = {
@ -42,13 +42,14 @@ export const ScopePicker = ({ clientId }: { clientId: string }) => {
[search],
);
const renderScopes = (scopes?: ScopeRepresentation[]) =>
scopes?.map((option) => (
const renderScopes = (scopes: ScopeRepresentation[]) =>
scopes.map((option) => (
<SelectOption key={option.id} value={option}>
{option.name}
</SelectOption>
));
if (!scopes) return <KeycloakSpinner />;
return (
<FormGroup
label={t("authorizationScopes")}
@ -62,7 +63,7 @@ export const ScopePicker = ({ clientId }: { clientId: string }) => {
defaultValue={[]}
control={control}
render={({ field }) => (
<Select
<KeycloakSelect
toggleId="scopes"
variant={SelectVariant.typeaheadMulti}
chipGroupProps={{
@ -70,14 +71,14 @@ export const ScopePicker = ({ clientId }: { clientId: string }) => {
expandedText: t("hide"),
collapsedText: t("showRemaining"),
}}
onToggle={(_event, val) => setOpen(val)}
onToggle={(val) => setOpen(val)}
isOpen={open}
selections={field.value.map((o: Scope) => o.name)}
onFilter={(_, value) => {
onFilter={(value) => {
setSearch(value);
return renderScopes(scopes);
}}
onSelect={(_, selectedValue) => {
onSelect={(selectedValue) => {
const option =
typeof selectedValue === "string"
? selectedValue
@ -89,15 +90,14 @@ export const ScopePicker = ({ clientId }: { clientId: string }) => {
: [...field.value, selectedValue];
field.onChange(changedValue);
}}
onClear={(event) => {
event.stopPropagation();
onClear={() => {
setSearch("");
field.onChange([]);
}}
typeAheadAriaLabel={t("authorizationScopes")}
>
{renderScopes(scopes)}
</Select>
</KeycloakSelect>
)}
/>
</FormGroup>

View file

@ -1,14 +1,14 @@
import type ScopeRepresentation from "@keycloak/keycloak-admin-client/lib/defs/scopeRepresentation";
import {
Select,
SelectOption,
SelectVariant,
} from "@patternfly/react-core/deprecated";
import { useRef, useState } from "react";
import { Controller, useFormContext } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { useAdminClient } from "../../admin-client";
import { useFetch } from "../../utils/useFetch";
import {
KeycloakSelect,
SelectVariant,
} from "../../components/select/KeycloakSelect";
import { SelectOption } from "@patternfly/react-core";
type ScopeSelectProps = {
clientId: string;
@ -87,11 +87,11 @@ export const ScopeSelect = ({
control={control}
rules={{ validate: (value) => value.length > 0 }}
render={({ field }) => (
<Select
<KeycloakSelect
toggleId="scopes"
variant={SelectVariant.typeaheadMulti}
onToggle={(_event, val) => setOpen(val)}
onFilter={(_, filter) => {
onToggle={(val) => setOpen(val)}
onFilter={(filter) => {
setSearch(filter);
return toSelectOptions(scopes);
}}
@ -99,8 +99,8 @@ export const ScopeSelect = ({
field.onChange([]);
setSearch("");
}}
selections={selectedScopes.map((s) => s.name)}
onSelect={(_, selectedValue) => {
selections={selectedScopes.map((s) => s.name!)}
onSelect={(selectedValue) => {
const option =
typeof selectedValue === "string"
? selectedScopes.find((s) => s.name === selectedValue)!
@ -120,7 +120,7 @@ export const ScopeSelect = ({
typeAheadAriaLabel={t("scopes")}
>
{toSelectOptions(scopes)}
</Select>
</KeycloakSelect>
)}
/>
);

View file

@ -1,10 +1,15 @@
import type PolicyProviderRepresentation from "@keycloak/keycloak-admin-client/lib/defs/policyProviderRepresentation";
import { ActionGroup, Button, Form } from "@patternfly/react-core";
import { Dropdown, DropdownToggle } from "@patternfly/react-core/deprecated";
import { SelectControl, TextControl } from "@keycloak/keycloak-ui-shared";
import {
ActionGroup,
Button,
Dropdown,
Form,
MenuToggle,
} from "@patternfly/react-core";
import { useEffect } from "react";
import { FormProvider, useForm } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { SelectControl, TextControl } from "@keycloak/keycloak-ui-shared";
import useToggle from "../../utils/useToggle";
import "./search-dropdown.css";
@ -50,18 +55,18 @@ export const SearchDropdown = ({
return (
<Dropdown
data-testid="searchdropdown_dorpdown"
className="pf-v5-u-ml-md"
toggle={
<DropdownToggle
onToggle={toggle}
toggle={(ref) => (
<MenuToggle
data-testid="searchdropdown_dorpdown"
ref={ref}
onClick={toggle}
className="keycloak__client_authentication__searchdropdown"
>
{type === "resource" && t("searchClientAuthorizationResource")}
{type === "policy" && t("searchClientAuthorizationPolicy")}
{type === "permission" && t("searchClientAuthorizationPermission")}
</DropdownToggle>
}
</MenuToggle>
)}
isOpen={open}
>
<FormProvider {...form}>

View file

@ -10,12 +10,11 @@ import {
ToolbarGroup,
ToolbarItem,
InputGroupItem,
} from "@patternfly/react-core";
import {
Select,
MenuToggle,
SelectList,
SelectOption,
SelectVariant,
} from "@patternfly/react-core/deprecated";
} from "@patternfly/react-core";
import { SearchIcon } from "@patternfly/react-icons";
import { Table, Th, Thead, Tr } from "@patternfly/react-table";
import { KeyboardEvent, useMemo, useState } from "react";
@ -115,38 +114,46 @@ export const Results = ({ evaluateResult, refresh, back }: ResultProps) => {
</ToolbarItem>
<ToolbarItem>
<Select
width={300}
data-testid="filter-type-select"
isOpen={filterDropdownOpen}
className="kc-filter-type-select"
variant={SelectVariant.single}
onToggle={toggleFilterDropdown}
toggle={(ref) => (
<MenuToggle
ref={ref}
onClick={toggleFilterDropdown}
isExpanded={filterDropdownOpen}
style={{ width: "300px" }}
>
{filter}
</MenuToggle>
)}
onSelect={(_, value) => {
setFilter(value as ResultsFilter);
toggleFilterDropdown();
refresh();
}}
selections={filter}
selected={filter}
>
<SelectOption
data-testid="all-results-option"
value={ResultsFilter.All}
isPlaceholder
>
{t("allResults")}
</SelectOption>
<SelectOption
data-testid="result-permit-option"
value={ResultsFilter.StatusPermitted}
>
{t("resultPermit")}
</SelectOption>
<SelectOption
data-testid="result-deny-option"
value={ResultsFilter.StatusDenied}
>
{t("resultDeny")}
</SelectOption>
<SelectList>
<SelectOption
data-testid="all-results-option"
value={ResultsFilter.All}
>
{t("allResults")}
</SelectOption>
<SelectOption
data-testid="result-permit-option"
value={ResultsFilter.StatusPermitted}
>
{t("resultPermit")}
</SelectOption>
<SelectOption
data-testid="result-deny-option"
value={ResultsFilter.StatusDenied}
>
{t("resultDeny")}
</SelectOption>
</SelectList>
</Select>
</ToolbarItem>
</ToolbarGroup>

View file

@ -1,4 +1,3 @@
import { SelectVariant } from "@patternfly/react-core/deprecated";
import { useTranslation } from "react-i18next";
import { ClientSelect } from "../../../components/client/ClientSelect";
@ -12,7 +11,7 @@ export const Client = () => {
helpText={t("policyClientHelp")}
required
defaultValue={[]}
variant={SelectVariant.typeaheadMulti}
variant="typeaheadMulti"
/>
);
};

View file

@ -4,9 +4,9 @@ import {
AlertVariant,
Button,
ButtonVariant,
DropdownItem,
PageSection,
} from "@patternfly/react-core";
import { DropdownItem } from "@patternfly/react-core/deprecated";
import { useState } from "react";
import { FormProvider, useForm } from "react-hook-form";
import { useTranslation } from "react-i18next";

View file

@ -6,6 +6,6 @@
width: max-content;
--pf-v5-c-form--m-horizontal__group-label--md--GridColumnWidth: 5rem;
--pf-v5-c-form--m-horizontal__group-control--md--GridColumnWidth: 24rem;
margin: 0 var(--pf-v5-global--spacer--lg) var(--pf-v5-global--spacer--lg)
margin: var(--pf-v5-global--spacer--lg) var(--pf-v5-global--spacer--lg)
var(--pf-v5-global--spacer--lg);
}

View file

@ -27,7 +27,8 @@ export const SignedJWT = ({ clientAuthenticatorType }: SignedJWTProps) => {
controller={{
defaultValue: "",
}}
maxHeight={200}
isScrollable
maxMenuHeight="200px"
options={[
{ key: "", value: t("anyAlgorithm") },
...providers.map((option) => ({ key: option, value: option })),

View file

@ -4,9 +4,9 @@ import {
ActionGroup,
Button,
ButtonVariant,
DropdownItem,
PageSection,
} from "@patternfly/react-core";
import { DropdownItem } from "@patternfly/react-core/deprecated";
import { useState } from "react";
import { FormProvider, useForm, useWatch } from "react-hook-form";
import { useTranslation } from "react-i18next";

View file

@ -2,18 +2,14 @@ import type ClientScopeRepresentation from "@keycloak/keycloak-admin-client/lib/
import {
Button,
ButtonVariant,
Dropdown,
DropdownItem,
DropdownList,
MenuToggle,
Modal,
ModalVariant,
} from "@patternfly/react-core";
import {
Dropdown,
DropdownDirection,
DropdownItem,
DropdownToggle,
Select,
SelectOption,
SelectVariant,
} from "@patternfly/react-core/deprecated";
} from "@patternfly/react-core";
import {
CaretDownIcon,
CaretUpIcon,
@ -30,6 +26,7 @@ import { ListEmptyState } from "../../components/list-empty-state/ListEmptyState
import { KeycloakDataTable } from "../../components/table-toolbar/KeycloakDataTable";
import useToggle from "../../utils/useToggle";
import { getProtocolName } from "../utils";
import { KeycloakSelect } from "../../components/select/KeycloakSelect";
import "./client-scopes.css";
@ -123,7 +120,7 @@ export const AddScopeDialog = ({
<SelectOption key={2} value={ProtocolType.OpenIDConnect}>
{t("protocolTypes.openid-connect")}
</SelectOption>,
<SelectOption key={3} value={ProtocolType.All} isPlaceholder>
<SelectOption key={3} value={ProtocolType.All}>
{t("protocolTypes.all")}
</SelectOption>,
];
@ -170,24 +167,29 @@ export const AddScopeDialog = ({
]
: [
<Dropdown
popperProps={{
direction: "up",
}}
className="keycloak__client-scopes-add__add-dropdown"
id="add-dropdown"
key="add-dropdown"
direction={DropdownDirection.up}
isOpen={addToggle}
toggle={
<DropdownToggle
toggle={(ref) => (
<MenuToggle
ref={ref}
isDisabled={rows.length === 0}
onToggle={() => setAddToggle(!addToggle)}
toggleVariant="primary"
toggleIndicator={CaretUpIcon}
id="add-scope-toggle"
onClick={() => setAddToggle(!addToggle)}
variant="primary"
id="add-dropdown"
statusIcon={<CaretUpIcon />}
>
{t("add")}
</DropdownToggle>
}
dropdownItems={clientScopeTypesDropdown(t, action)}
/>,
</MenuToggle>
)}
>
<DropdownList>
{clientScopeTypesDropdown(t, action)}
</DropdownList>
</Dropdown>,
<Button
id="modal-cancel"
key="cancel"
@ -214,27 +216,29 @@ export const AddScopeDialog = ({
onSelect={() => {
onFilterTypeDropdownSelect(filterType);
}}
data-testid="filter-type-dropdown"
toggle={
<DropdownToggle
toggle={(ref) => (
<MenuToggle
ref={ref}
data-testid="filter-type-dropdown"
id="toggle-id-9"
onToggle={toggleIsFilterTypeDropdownOpen}
toggleIndicator={CaretDownIcon}
onClick={toggleIsFilterTypeDropdownOpen}
icon={<FilterIcon />}
statusIcon={<CaretDownIcon />}
>
{filterType}
</DropdownToggle>
}
</MenuToggle>
)}
isOpen={isFilterTypeDropdownOpen}
dropdownItems={[
>
<DropdownList>
<DropdownItem
data-testid="filter-type-dropdown-item"
key="filter-type"
>
{filterType === FilterType.Name ? t("protocol") : t("name")}
</DropdownItem>,
]}
/>
</DropdownItem>
</DropdownList>
</Dropdown>
}
toolbarItem={
filterType === FilterType.Protocol && (
@ -244,39 +248,40 @@ export const AddScopeDialog = ({
onFilterTypeDropdownSelect(filterType);
}}
data-testid="filter-type-dropdown"
toggle={
<DropdownToggle
toggle={(ref) => (
<MenuToggle
ref={ref}
id="toggle-id-9"
onToggle={toggleIsFilterTypeDropdownOpen}
toggleIndicator={CaretDownIcon}
onClick={toggleIsFilterTypeDropdownOpen}
statusIcon={<CaretDownIcon />}
icon={<FilterIcon />}
>
{filterType}
</DropdownToggle>
}
</MenuToggle>
)}
isOpen={isFilterTypeDropdownOpen}
dropdownItems={[
>
<DropdownList>
<DropdownItem
data-testid="filter-type-dropdown-item"
key="filter-type"
>
{t("name")}
</DropdownItem>,
]}
/>
<Select
variant={SelectVariant.single}
</DropdownItem>
</DropdownList>
</Dropdown>
<KeycloakSelect
className="kc-protocolType-select"
aria-label={t("selectOne")}
onToggle={toggleIsProtocolTypeDropdownOpen}
onSelect={(_, value) =>
onSelect={(value) =>
onProtocolTypeDropdownSelect(value.toString())
}
selections={protocolType}
isOpen={isProtocolTypeDropdownOpen}
>
{protocolTypeOptions}
</Select>
</KeycloakSelect>
</>
)
}

View file

@ -3,13 +3,13 @@ import {
AlertVariant,
Button,
ButtonVariant,
ToolbarItem,
} from "@patternfly/react-core";
import {
Dropdown,
DropdownItem,
KebabToggle,
} from "@patternfly/react-core/deprecated";
DropdownList,
MenuToggle,
ToolbarItem,
} from "@patternfly/react-core";
import { EllipsisVIcon } from "@patternfly/react-icons";
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { Link } from "react-router-dom";
@ -297,12 +297,21 @@ export const ClientScopes = ({
</ToolbarItem>
<ToolbarItem>
<Dropdown
toggle={
<KebabToggle onToggle={() => setKebabOpen(!kebabOpen)} />
}
toggle={(ref) => (
<MenuToggle
data-testid="kebab"
aria-label="Kebab toggle"
ref={ref}
variant="plain"
onClick={() => setKebabOpen(!kebabOpen)}
isExpanded={kebabOpen}
>
<EllipsisVIcon />
</MenuToggle>
)}
isOpen={kebabOpen}
isPlain
dropdownItems={[
>
<DropdownList>
<DropdownItem
key="deleteAll"
isDisabled={selectedRows.length === 0}
@ -329,9 +338,9 @@ export const ClientScopes = ({
}}
>
{t("remove")}
</DropdownItem>,
]}
/>
</DropdownItem>
</DropdownList>
</Dropdown>
</ToolbarItem>
</>
)}

View file

@ -9,6 +9,7 @@ import {
Grid,
GridItem,
PageSection,
SelectOption,
Split,
SplitItem,
Tab,
@ -18,11 +19,6 @@ import {
Text,
TextContent,
} from "@patternfly/react-core";
import {
Select,
SelectOption,
SelectVariant,
} from "@patternfly/react-core/deprecated";
import { QuestionCircleIcon } from "@patternfly/react-icons";
import { useEffect, useRef, useState } from "react";
import { FormProvider, useForm } from "react-hook-form";
@ -39,6 +35,10 @@ import { useFetch } from "../../utils/useFetch";
import { GeneratedCodeTab } from "./GeneratedCodeTab";
import "./evaluate.css";
import {
KeycloakSelect,
SelectVariant,
} from "../../components/select/KeycloakSelect";
export type EvaluateScopesProps = {
clientId: string;
@ -250,14 +250,14 @@ export const EvaluateScopes = ({ clientId, protocol }: EvaluateScopesProps) => {
>
<Split hasGutter>
<SplitItem isFilled>
<Select
<KeycloakSelect
toggleId="scopeParameter"
variant={SelectVariant.typeaheadMulti}
typeAheadAriaLabel={t("scopeParameter")}
onToggle={() => setIsScopeOpen(!isScopeOpen)}
isOpen={isScopeOpen}
selections={selected}
onSelect={(_, value) => {
onSelect={(value) => {
const option = value as string;
if (selected.includes(option)) {
if (option !== prefix) {
@ -273,7 +273,7 @@ export const EvaluateScopes = ({ clientId, protocol }: EvaluateScopesProps) => {
{selectableScopes.map((option, index) => (
<SelectOption key={index} value={option.name} />
))}
</Select>
</KeycloakSelect>
</SplitItem>
<SplitItem>
<ClipboardCopy className="keycloak__scopes_evaluate__clipboard-copy">

View file

@ -1,8 +1,4 @@
.keycloak__client-scopes__searchtype button {
width: 200px;
}
.keycloak__client-scopes-add__add-dropdown {
margin-right: var(--pf-v5-global--spacer--md);
}

View file

@ -1,16 +1,17 @@
import type ClientScopeRepresentation from "@keycloak/keycloak-admin-client/lib/defs/clientScopeRepresentation";
import KeycloakAdminClient from "@keycloak/keycloak-admin-client";
import {
DropdownItem,
Select,
SelectOption,
SelectProps,
} from "@patternfly/react-core/deprecated";
import type { TFunction } from "i18next";
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { toUpperCase } from "../../util";
import {
DropdownItem,
MenuToggle,
Select,
SelectOption,
SelectProps,
} from "@patternfly/react-core";
export enum ClientScope {
default = "default",
@ -50,11 +51,12 @@ export const clientScopeTypesDropdown = (
</DropdownItem>
));
type CellDropdownProps = Omit<SelectProps, "onToggle"> & {
type CellDropdownProps = Omit<SelectProps, "toggle"> & {
clientScope: ClientScopeRepresentation;
type: ClientScopeType | AllClientScopeType;
all?: boolean;
onSelect: (value: ClientScopeType | AllClientScopeType) => void;
isDisabled?: boolean;
};
export const CellDropdown = ({
@ -62,6 +64,7 @@ export const CellDropdown = ({
type,
onSelect,
all = false,
isDisabled,
...props
}: CellDropdownProps) => {
const { t } = useTranslation();
@ -69,11 +72,21 @@ export const CellDropdown = ({
return (
<Select
className={`keycloak__client-scope__${type}`}
key={clientScope.id}
onToggle={() => setOpen(!open)}
toggle={(ref) => (
<MenuToggle
data-testid="cell-dropdown"
className={`keycloak__client-scope__${type}`}
ref={ref}
onClick={() => setOpen(!open)}
isExpanded={open}
isDisabled={isDisabled}
>
{t(`clientScopeType.${type}`)}
</MenuToggle>
)}
isOpen={open}
selections={[type]}
selected={[type]}
onSelect={(_, value) => {
onSelect(
all ? (value as ClientScopeType) : (value as AllClientScopeType),

View file

@ -1,14 +1,13 @@
import type ClientRepresentation from "@keycloak/keycloak-admin-client/lib/defs/clientRepresentation";
import type { ClientQuery } from "@keycloak/keycloak-admin-client/lib/resources/clients";
import { SelectProps, SelectVariant } from "@patternfly/react-core/deprecated";
import { SelectControl, SelectVariant } from "@keycloak/keycloak-ui-shared";
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { SelectControl } from "@keycloak/keycloak-ui-shared";
import { useAdminClient } from "../../admin-client";
import { useFetch } from "../../utils/useFetch";
import type { ComponentProps } from "../dynamic/components";
type ClientSelectProps = ComponentProps & Pick<SelectProps, "variant">;
type ClientSelectProps = ComponentProps & { variant?: `${SelectVariant}` };
export const ClientSelect = ({
name,
@ -17,7 +16,7 @@ export const ClientSelect = ({
defaultValue,
isDisabled = false,
required = false,
variant = SelectVariant.typeahead,
variant = "typeahead",
}: ClientSelectProps) => {
const { adminClient } = useAdminClient();

View file

@ -1,21 +1,20 @@
import { fetchWithError } from "@keycloak/keycloak-admin-client";
import { HelpItem, useHelp } from "@keycloak/keycloak-ui-shared";
import {
Form,
FormGroup,
MenuToggle,
ModalVariant,
Select,
SelectList,
SelectOption,
Stack,
StackItem,
TextArea,
} from "@patternfly/react-core";
import {
Select,
SelectOption,
SelectVariant,
} from "@patternfly/react-core/deprecated";
import { saveAs } from "file-saver";
import { useEffect, useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
import { HelpItem, useHelp } from "@keycloak/keycloak-ui-shared";
import { useAdminClient } from "../../admin-client";
import { useRealm } from "../../context/realm-context/RealmContext";
import { useServerInfo } from "../../context/server-info/ServerInfoProvider";
@ -125,29 +124,39 @@ export const DownloadDialog = ({
}
>
<Select
toggleId="type"
isOpen={openType}
onToggle={(_event, isExpanded) => setOpenType(isExpanded)}
variant={SelectVariant.single}
value={selected}
selections={selected}
toggle={(ref) => (
<MenuToggle
id="type"
ref={ref}
onClick={() => setOpenType(!openType)}
isExpanded={openType}
>
{selected}
</MenuToggle>
)}
selected={selected}
onSelect={(_, value) => {
setSelected(value.toString());
setSelected(value?.toString() || "");
setOpenType(false);
}}
aria-label="Select Input"
menuAppendTo={() => document.body}
aria-label={t("selectOne")}
popperProps={{
appendTo: document.body,
}}
>
{configFormats.map((configFormat) => (
<SelectOption
key={configFormat.id}
value={configFormat.id}
isSelected={selected === configFormat.id}
description={enabled ? configFormat.helpText : undefined}
>
{configFormat.displayType}
</SelectOption>
))}
<SelectList>
{configFormats.map((configFormat) => (
<SelectOption
key={configFormat.id}
value={configFormat.id}
isSelected={selected === configFormat.id}
description={enabled ? configFormat.helpText : undefined}
>
{configFormat.displayType}
</SelectOption>
))}
</SelectList>
</Select>
</FormGroup>
</StackItem>

View file

@ -1,16 +1,11 @@
import { FormGroup } from "@patternfly/react-core";
import {
Select,
SelectOption,
SelectVariant,
} from "@patternfly/react-core/deprecated";
import { HelpItem } from "@keycloak/keycloak-ui-shared";
import { FormGroup, SelectOption } from "@patternfly/react-core";
import { useState } from "react";
import { Controller, useFormContext } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { HelpItem } from "@keycloak/keycloak-ui-shared";
import type { ComponentProps } from "./components";
import { convertToName } from "./DynamicComponents";
import type { ComponentProps } from "./components";
import { KeycloakSelect, SelectVariant } from "../select/KeycloakSelect";
export const ListComponent = ({
name,
@ -38,11 +33,11 @@ export const ListComponent = ({
defaultValue={defaultValue || options?.[0] || ""}
control={control}
render={({ field }) => (
<Select
<KeycloakSelect
toggleId={name}
isDisabled={isDisabled}
onToggle={(_event, toggle) => setOpen(toggle)}
onSelect={(_, value) => {
onToggle={(toggle) => setOpen(toggle)}
onSelect={(value) => {
field.onChange(value as string);
setOpen(false);
}}
@ -56,9 +51,11 @@ export const ListComponent = ({
selected={option === field.value}
key={option}
value={option}
/>
>
{option}
</SelectOption>
))}
</Select>
</KeycloakSelect>
)}
/>
</FormGroup>

View file

@ -1,16 +1,11 @@
import { FormGroup } from "@patternfly/react-core";
import {
Select,
SelectOption,
SelectVariant,
} from "@patternfly/react-core/deprecated";
import { HelpItem } from "@keycloak/keycloak-ui-shared";
import { FormGroup, SelectOption } from "@patternfly/react-core";
import { useState } from "react";
import { Controller, useFormContext } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { HelpItem } from "@keycloak/keycloak-ui-shared";
import type { ComponentProps } from "./components";
import { KeycloakSelect, SelectVariant } from "../select/KeycloakSelect";
import { convertToName } from "./DynamicComponents";
import type { ComponentProps } from "./components";
function stringToMultiline(value?: string): string[] {
return typeof value === "string" && value.length > 0 ? value.split("##") : [];
@ -46,7 +41,7 @@ export const MultiValuedListComponent = ({
control={control}
defaultValue={defaultValue ? [defaultValue] : []}
render={({ field }) => (
<Select
<KeycloakSelect
toggleId={name}
data-testid={name}
isDisabled={isDisabled}
@ -57,11 +52,11 @@ export const MultiValuedListComponent = ({
}}
variant={SelectVariant.typeaheadMulti}
typeAheadAriaLabel="Select"
onToggle={(_event, isOpen) => setOpen(isOpen)}
onToggle={(isOpen) => setOpen(isOpen)}
selections={
stringify ? stringToMultiline(field.value) : field.value
}
onSelect={(_, v) => {
onSelect={(v) => {
const option = v.toString();
const values = stringify
? stringToMultiline(field.value)
@ -74,17 +69,18 @@ export const MultiValuedListComponent = ({
}
field.onChange(stringify ? toStringValue(newValue) : newValue);
}}
onClear={(event) => {
event.stopPropagation();
onClear={() => {
field.onChange(stringify ? "" : []);
}}
isOpen={open}
aria-label={t(label!)}
>
{options?.map((option) => (
<SelectOption key={option} value={option} />
<SelectOption key={option} value={option}>
{option}
</SelectOption>
))}
</Select>
</KeycloakSelect>
)}
/>
</FormGroup>

View file

@ -1,15 +1,14 @@
import {
Divider,
Dropdown,
DropdownItem,
DropdownList,
MenuToggle,
Split,
SplitItem,
Switch,
TextContent,
} from "@patternfly/react-core";
import {
Dropdown,
DropdownItem,
DropdownToggle,
} from "@patternfly/react-core/deprecated";
import { ExternalLinkAltIcon, HelpIcon } from "@patternfly/react-icons";
import { useState } from "react";
import { useTranslation } from "react-i18next";
@ -60,20 +59,23 @@ export const HelpHeader = () => {
];
return (
<Dropdown
position="right"
isPlain
popperProps={{
position: "right",
}}
isOpen={open}
toggle={
<DropdownToggle
toggleIndicator={null}
onToggle={() => setOpen(!open)}
toggle={(ref) => (
<MenuToggle
ref={ref}
variant="plain"
onClick={() => setOpen(!open)}
aria-label="Help"
id="help"
>
<HelpIcon />
</DropdownToggle>
}
dropdownItems={dropdownItems}
/>
</MenuToggle>
)}
>
<DropdownList>{dropdownItems}</DropdownList>
</Dropdown>
);
};

View file

@ -1,11 +1,15 @@
import { Grid, GridItem, TextInput } from "@patternfly/react-core";
import { Select, SelectOption } from "@patternfly/react-core/deprecated";
import {
Grid,
GridItem,
SelectOption,
TextInput,
} from "@patternfly/react-core";
import { useState } from "react";
import { UseControllerProps, useController } from "react-hook-form";
import { useTranslation } from "react-i18next";
import useToggle from "../../utils/useToggle";
import { DefaultValue } from "./KeyValueInput";
import { KeycloakSelect } from "../select/KeycloakSelect";
type KeySelectProp = UseControllerProps & {
selectItems: DefaultValue[];
@ -22,10 +26,10 @@ export const KeySelect = ({ selectItems, ...rest }: KeySelectProp) => {
return (
<Grid>
<GridItem lg={custom ? 2 : 12}>
<Select
<KeycloakSelect
onToggle={() => toggle()}
isOpen={open}
onSelect={(_, value) => {
onSelect={(value) => {
if (value) {
setCustom(false);
}
@ -44,7 +48,7 @@ export const KeySelect = ({ selectItems, ...rest }: KeySelectProp) => {
</SelectOption>
)),
]}
</Select>
</KeycloakSelect>
</GridItem>
{custom && (
<GridItem lg={10}>

View file

@ -1,9 +1,8 @@
import { TextInput } from "@patternfly/react-core";
import { Select, SelectOption } from "@patternfly/react-core/deprecated";
import { SelectOption, TextInput } from "@patternfly/react-core";
import { useMemo, useState } from "react";
import { UseControllerProps, useController } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { KeycloakSelect } from "../select/KeycloakSelect";
import { DefaultValue } from "./KeyValueInput";
type ValueSelectProps = UseControllerProps & {
@ -26,20 +25,20 @@ export const ValueSelect = ({
);
return defaultItem?.values ? (
<Select
onToggle={(_event, isOpen) => setOpen(isOpen)}
<KeycloakSelect
onToggle={(isOpen) => setOpen(isOpen)}
isOpen={open}
onSelect={(_, value) => {
onSelect={(value) => {
field.onChange(value);
setOpen(false);
}}
selections={field.value ? [field.value] : t("choose")}
placeholder={t("valuePlaceholder")}
placeholderText={t("valuePlaceholder")}
>
{defaultItem.values.map((item) => (
<SelectOption key={item} value={item} />
))}
</Select>
</KeycloakSelect>
) : (
<TextInput
aria-label={t("customValue")}

View file

@ -4,15 +4,19 @@ import {
CardFooter,
CardHeader,
CardTitle,
Dropdown,
DropdownList,
Flex,
FlexItem,
Label,
MenuToggle,
MenuToggleElement,
} from "@patternfly/react-core";
import { Dropdown, KebabToggle } from "@patternfly/react-core/deprecated";
import { ReactElement, useState } from "react";
import { Link, To } from "react-router-dom";
import "./keycloak-card.css";
import { EllipsisVIcon } from "@patternfly/react-icons";
export type KeycloakCardProps = {
title: string;
@ -38,18 +42,28 @@ export const KeycloakCard = ({
};
return (
<Card isSelectable>
<Card isSelectable isClickable>
<CardHeader
actions={{
actions: dropdownItems ? (
<Dropdown
data-testid={`${title}-dropdown`}
isPlain
position={"right"}
toggle={<KebabToggle onToggle={onDropdownToggle} />}
popperProps={{
position: "right",
}}
toggle={(ref: React.Ref<MenuToggleElement>) => (
<MenuToggle
ref={ref}
onClick={onDropdownToggle}
variant="plain"
data-testid={`${title}-dropdown`}
>
<EllipsisVIcon />
</MenuToggle>
)}
isOpen={isDropdownOpen}
dropdownItems={dropdownItems}
/>
>
<DropdownList>{dropdownItems}</DropdownList>
</Dropdown>
) : undefined,
hasNoOffset: false,
className: undefined,

View file

@ -1,30 +1,27 @@
import { label } from "@keycloak/keycloak-ui-shared";
import {
Button,
Divider,
Dropdown,
DropdownGroup,
DropdownItem,
DropdownList,
Label,
MenuToggle,
SearchInput,
Spinner,
Split,
SplitItem,
Stack,
StackItem,
} from "@patternfly/react-core";
import {
ContextSelector,
ContextSelectorItem,
ContextSelectorItemProps,
Dropdown,
DropdownItem,
DropdownToggle,
} from "@patternfly/react-core/deprecated";
import { CheckIcon } from "@patternfly/react-icons";
import { Fragment, useState, useMemo } from "react";
import { Fragment, useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
import { Link, To, useHref } from "react-router-dom";
import { label } from "@keycloak/keycloak-ui-shared";
import { useRealm } from "../../context/realm-context/RealmContext";
import { Link, useNavigate } from "react-router-dom";
import { useRealms } from "../../context/RealmsContext";
import { useRecentRealms } from "../../context/RecentRealms";
import { useRealm } from "../../context/realm-context/RealmContext";
import { useWhoAmI } from "../../context/whoami/WhoAmI";
import { toDashboard } from "../../dashboard/routes/Dashboard";
import { toAddRealm } from "../../realm/routes/AddRealm";
@ -83,23 +80,6 @@ const RealmText = ({ name, displayName, showIsRecent }: RealmTextProps) => {
);
};
// We need to make all these props partial because of a bug in PatternFly.
// See: https://github.com/patternfly/patternfly-react/pull/8670
// TODO: Remove this partial when a fix has been released.
type ContextSelectorItemLinkProps = Partial<
Omit<ContextSelectorItemProps, "href">
> & {
to: To;
};
const ContextSelectorItemLink = ({
to,
...props
}: ContextSelectorItemLinkProps) => {
const href = useHref(to);
return <ContextSelectorItem {...props} href={href} />;
};
export const RealmSelector = () => {
const { realm } = useRealm();
const { realms } = useRealms();
@ -108,11 +88,11 @@ export const RealmSelector = () => {
const [search, setSearch] = useState("");
const { t } = useTranslation();
const recentRealms = useRecentRealms();
const navigate = useNavigate();
const all = useMemo(
() =>
realms
.filter((r) => r.name !== realm)
.map((realm) => {
const used = recentRealms.some((name) => name === realm.name);
return { realm, used };
@ -149,82 +129,72 @@ export const RealmSelector = () => {
[realm, realms],
);
return realms.length > 5 ? (
<ContextSelector
data-testid="realmSelector"
toggleText={label(t, realmDisplayName, realm)}
isOpen={open}
screenReaderLabel={label(t, realmDisplayName, realm)}
onToggle={() => setOpen(!open)}
searchInputValue={search}
onSearchInputChange={(event, value) => setSearch(value)}
className="keycloak__realm_selector__context_selector"
footer={
whoAmI.canCreateRealm() && (
<ContextSelectorItem key="add">
<AddRealm onClick={() => setOpen(false)} />
</ContextSelectorItem>
)
}
>
{filteredItems.map((item) => (
<ContextSelectorItemLink
key={item.realm.name}
to={toDashboard({ realm: item.realm.name })}
onClick={() => setOpen(false)}
>
<RealmText {...item.realm} showIsRecent={item.used} />{" "}
</ContextSelectorItemLink>
))}
</ContextSelector>
) : (
return (
<Dropdown
id="realm-select"
data-testid="realmSelector"
className="keycloak__realm_selector__dropdown"
isOpen={open}
toggle={
<DropdownToggle
data-testid="realmSelectorToggle"
onToggle={() => {
toggle={(ref) => (
<MenuToggle
ref={ref}
data-testid="realmSelector"
onClick={() => {
setOpen(!open);
}}
className="keycloak__realm_selector_dropdown__toggle"
isFullWidth
>
{label(t, realmDisplayName, realm)}
</DropdownToggle>
}
dropdownItems={(realms.length !== 0
? realms.map((realm) => (
<DropdownItem
key={realm.name}
component={
<Link
to={toDashboard({ realm: realm.name })}
onClick={() => setOpen(false)}
>
<RealmText {...realm} />
</Link>
}
/>
))
: [
<DropdownItem key="loader">
<Spinner size="sm" /> {t("loadingRealms")}
</DropdownItem>,
]
).concat([
<Fragment key="add-realm">
{whoAmI.canCreateRealm() && (
<>
<Divider key="divider" />
<DropdownItem key="add" component="div">
<AddRealm onClick={() => setOpen(false)} />
</MenuToggle>
)}
>
<DropdownList>
{realms.length > 5 && (
<>
<DropdownGroup>
<DropdownList>
<SearchInput
value={search}
onChange={(_, value) => setSearch(value)}
onClear={() => setSearch("")}
/>
</DropdownList>
</DropdownGroup>
<Divider component="li" />
</>
)}
{(realms.length !== 0
? filteredItems.map((i) => (
<DropdownItem
key={i.realm.name}
onClick={() => {
navigate(toDashboard({ realm: i.realm.name }));
setOpen(false);
}}
>
<RealmText
{...i.realm}
showIsRecent={realms.length > 5 && i.used}
/>
</DropdownItem>
</>
)}
</Fragment>,
])}
/>
))
: [
<DropdownItem key="loader">
<Spinner size="sm" /> {t("loadingRealms")}
</DropdownItem>,
]
).concat([
<Fragment key="add-realm">
{whoAmI.canCreateRealm() && (
<>
<Divider key="divider" />
<DropdownItem key="add" component="div">
<AddRealm onClick={() => setOpen(false)} />
</DropdownItem>
</>
)}
</Fragment>,
])}
</DropdownList>
</Dropdown>
);
};

View file

@ -1,45 +1,13 @@
.keycloak__realm_selector_dropdown__toggle {
--pf-v5-c-dropdown__toggle--Color: var(--pf-v5-c-nav__link--m-current--Color);
--pf-v5-c-dropdown__toggle--BackgroundColor: var(
--pf-v5-global--BackgroundColor--dark-100
);
--pf-v5-c-dropdown__toggle--before--BorderTopColor: var(
--pf-v5-global--BorderColor--200
);
--pf-v5-c-dropdown__toggle--before--BorderRightColor: var(
--pf-v5-global--BorderColor--200
);
--pf-v5-c-dropdown__toggle--before--BorderBottomColor: var(
--pf-v5-global--BorderColor--100
);
--pf-v5-c-dropdown__toggle--before--BorderLeftColor: var(
--pf-v5-global--BorderColor--200
);
width: 100%;
.keycloak__realm_selector__dropdown .pf-v5-c-menu__list .pf-m-fill {
overflow: hidden;
text-overflow: ellipsis;
}
.keycloak__realm_selector__dropdown .pf-v5-c-dropdown__menu {
.keycloak__realm_selector__dropdown .pf-v5-c-menu__list {
min-width: 0;
width: 15.125rem;
}
.keycloak__realm_selector__dropdown .pf-v5-c-dropdown__menu .pf-m-fill {
overflow: hidden;
text-overflow: ellipsis;
}
.pf-v5-c-context-selector.keycloak__realm_selector__context_selector {
--pf-v5-c-context-selector__toggle--Color: var(--pf-v5-c-nav__link--m-current--Color);
}
.keycloak__realm_selector__dropdown {
width: 100%;
}
.keycloak__realm_selector__list-item-split {
width: 100%;
text-align: left;
}
.pf-v5-c-nav__item.keycloak__page_nav__nav_item__realm-selector {
margin-top: var(--pf-v5-c-nav__link--PaddingTop);

View file

@ -1,14 +1,13 @@
import {
Button,
Dropdown,
DropdownItem,
DropdownList,
MenuToggle,
Modal,
ModalVariant,
ToolbarItem,
} from "@patternfly/react-core";
import {
Dropdown,
DropdownItem,
DropdownToggle,
} from "@patternfly/react-core/deprecated";
import { FilterIcon } from "@patternfly/react-icons";
import { useState } from "react";
import { useTranslation } from "react-i18next";
@ -150,26 +149,28 @@ export const AddRoleMappingModal = ({
setSearchToggle(false);
refresh();
}}
data-testid="filter-type-dropdown"
toggle={
<DropdownToggle
onToggle={(_event, val) => setSearchToggle(val)}
toggle={(ref) => (
<MenuToggle
data-testid="filter-type-dropdown"
ref={ref}
onClick={() => setSearchToggle(!searchToggle)}
icon={<FilterIcon />}
>
{filterType === "roles"
? t("filterByRoles")
: t("filterByClients")}
</DropdownToggle>
}
</MenuToggle>
)}
isOpen={searchToggle}
dropdownItems={[
>
<DropdownList>
<DropdownItem key="filter-type" data-testid={filterType}>
{filterType === "roles"
? t("filterByClients")
: t("filterByRoles")}
</DropdownItem>,
]}
/>
</DropdownItem>
</DropdownList>
</Dropdown>
</ToolbarItem>
)
}

View file

@ -0,0 +1,49 @@
import { ChipGroupProps, SelectProps } from "@patternfly/react-core";
import { SingleSelect } from "./SingleSelect";
import { TypeaheadSelect } from "./TypeaheadSelect";
export type Variant = `${SelectVariant}`;
export enum SelectVariant {
single = "single",
typeahead = "typeahead",
typeaheadMulti = "typeaheadMulti",
}
export const propertyToString = (prop: string | number | undefined) =>
typeof prop === "number" ? prop + "px" : prop;
export type KeycloakSelectProps<> = Omit<
SelectProps,
"name" | "toggle" | "selected" | "onClick" | "onSelect"
> & {
toggleId?: string;
onFilter?: (value: string) => JSX.Element[];
onClear?: () => void;
variant?: Variant;
isDisabled?: boolean;
menuAppendTo?: string;
maxHeight?: string | number;
width?: string | number;
toggleIcon?: React.ReactElement;
direction?: "up" | "down";
placeholderText?: string;
onSelect?: (value: string | number | object) => void;
onToggle: (val: boolean) => void;
selections?: string | string[] | number | number[];
validated?: "success" | "warning" | "error" | "default";
typeAheadAriaLabel?: string;
chipGroupProps?: Omit<ChipGroupProps, "children" | "ref">;
chipGroupComponent?: React.ReactNode;
footer?: React.ReactNode;
};
export const KeycloakSelect = ({
variant = SelectVariant.single,
...rest
}: KeycloakSelectProps) => {
if (variant === SelectVariant.single) {
return <SingleSelect {...rest} />;
} else {
return <TypeaheadSelect {...rest} variant={variant} />;
}
};

View file

@ -0,0 +1,84 @@
import {
MenuToggle,
Select,
SelectList,
SelectOptionProps,
} from "@patternfly/react-core";
import { Children, useRef, useState } from "react";
import { KeycloakSelectProps, propertyToString } from "./KeycloakSelect";
type SingleSelectProps = Omit<KeycloakSelectProps, "variant">;
export const SingleSelect = ({
toggleId,
onToggle,
onSelect,
selections,
isOpen,
menuAppendTo,
direction,
width,
maxHeight,
toggleIcon,
className,
children,
...props
}: SingleSelectProps) => {
const [open, setOpen] = useState(false);
const ref = useRef<HTMLElement>();
const toggle = () => {
setOpen(!open);
onToggle(!open);
};
const append = () => {
if (menuAppendTo === "parent") {
return ref.current?.parentElement || "inline";
}
return "inline";
};
const childArray = Children.toArray(
children,
) as React.ReactElement<SelectOptionProps>[];
return (
<Select
ref={ref}
maxMenuHeight={propertyToString(maxHeight)}
isScrollable
popperProps={{
appendTo: append(),
direction,
width: propertyToString(width),
}}
{...props}
onClick={toggle}
selected={selections}
onSelect={(_, value) => {
onSelect?.(value || "");
toggle();
}}
toggle={(ref) => (
<MenuToggle
id={toggleId}
ref={ref}
className={className}
onClick={toggle}
isExpanded={isOpen}
aria-label={props["aria-label"]}
icon={toggleIcon}
isFullWidth
>
{childArray.find((c) => c.props.value === selections)?.props
.children ||
selections ||
props["aria-label"]}
</MenuToggle>
)}
isOpen={isOpen}
>
<SelectList>{children}</SelectList>
</Select>
);
};

View file

@ -0,0 +1,184 @@
import {
Button,
Chip,
ChipGroup,
MenuFooter,
MenuToggle,
MenuToggleStatus,
Select,
SelectList,
SelectOptionProps,
TextInputGroup,
TextInputGroupMain,
TextInputGroupUtilities,
} from "@patternfly/react-core";
import { TimesIcon } from "@patternfly/react-icons";
import { Children, useRef, useState } from "react";
import {
KeycloakSelectProps,
SelectVariant,
propertyToString,
} from "./KeycloakSelect";
export const TypeaheadSelect = ({
toggleId,
onSelect,
onToggle,
onFilter,
variant,
validated,
placeholderText,
maxHeight,
width,
toggleIcon,
direction,
selections,
typeAheadAriaLabel,
chipGroupComponent,
chipGroupProps,
footer,
children,
...rest
}: KeycloakSelectProps) => {
const [filterValue, setFilterValue] = useState("");
const [focusedItemIndex, setFocusedItemIndex] = useState<number>(0);
const textInputRef = useRef<HTMLInputElement>();
const childArray = Children.toArray(
children,
) as React.ReactElement<SelectOptionProps>[];
const toggle = () => {
onToggle?.(!rest.isOpen);
};
const onInputKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {
const focusedItem = childArray[focusedItemIndex];
onToggle?.(true);
switch (event.key) {
case "Enter": {
event.preventDefault();
if (variant !== SelectVariant.typeaheadMulti) {
setFilterValue(focusedItem.props.value);
} else {
setFilterValue("");
}
onSelect?.(focusedItem.props.value);
onToggle?.(false);
setFocusedItemIndex(0);
break;
}
case "Escape": {
onToggle?.(false);
break;
}
case "ArrowUp":
case "ArrowDown": {
event.preventDefault();
let indexToFocus = 0;
if (event.key === "ArrowUp") {
if (focusedItemIndex === 0) {
indexToFocus = childArray.length - 1;
} else {
indexToFocus = focusedItemIndex - 1;
}
}
if (event.key === "ArrowDown") {
if (focusedItemIndex === childArray.length - 1) {
indexToFocus = 0;
} else {
indexToFocus = focusedItemIndex + 1;
}
}
setFocusedItemIndex(indexToFocus);
break;
}
}
};
return (
<Select
{...rest}
onClick={toggle}
onSelect={(_, value) => onSelect?.(value || "")}
maxMenuHeight={propertyToString(maxHeight)}
popperProps={{ direction, width: propertyToString(width) }}
toggle={(ref) => (
<MenuToggle
ref={ref}
id={toggleId}
variant="typeahead"
onClick={() => onToggle?.(true)}
icon={toggleIcon}
isExpanded={rest.isOpen}
isFullWidth
status={validated === "error" ? MenuToggleStatus.danger : undefined}
>
<TextInputGroup isPlain>
<TextInputGroupMain
placeholder={placeholderText}
value={filterValue}
onClick={toggle}
onChange={(_, value) => {
setFilterValue(value);
onFilter?.(value);
}}
onKeyDown={(event) => onInputKeyDown(event)}
autoComplete="off"
innerRef={textInputRef}
role="combobox"
isExpanded={rest.isOpen}
aria-controls="select-typeahead-listbox"
aria-label={typeAheadAriaLabel}
>
{variant === SelectVariant.typeaheadMulti &&
Array.isArray(selections) &&
(chipGroupComponent ? (
chipGroupComponent
) : (
<ChipGroup {...chipGroupProps}>
{selections.map((selection, index: number) => (
<Chip
key={index}
onClick={(ev) => {
ev.stopPropagation();
onSelect?.(selection);
}}
>
{selection}
</Chip>
))}
</ChipGroup>
))}
</TextInputGroupMain>
<TextInputGroupUtilities>
{!!filterValue && (
<Button
variant="plain"
onClick={() => {
onSelect?.("");
setFilterValue("");
textInputRef?.current?.focus();
}}
aria-label="Clear input value"
>
<TimesIcon aria-hidden />
</Button>
)}
</TextInputGroupUtilities>
</TextInputGroup>
</MenuToggle>
)}
>
<SelectList>{children}</SelectList>
{footer && <MenuFooter>{footer}</MenuFooter>}
</Select>
);
};

View file

@ -1,17 +1,17 @@
import { useEffect, useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
import {
SelectOption,
Split,
SplitItem,
TextInput,
TextInputProps,
} from "@patternfly/react-core";
import {
DropdownProps,
Select,
SelectOption,
KeycloakSelect,
KeycloakSelectProps,
SelectVariant,
} from "@patternfly/react-core/deprecated";
} from "../select/KeycloakSelect";
export type Unit = "second" | "minute" | "hour" | "day";
@ -28,7 +28,7 @@ export type TimeSelectorProps = Omit<
TextInputProps,
"onChange" | "defaultValue"
> &
Pick<DropdownProps, "menuAppendTo"> & {
Pick<KeycloakSelectProps, "menuAppendTo"> & {
value?: number;
units?: Unit[];
onChange?: (time: number | string) => void;
@ -131,17 +131,17 @@ export const TimeSelector = ({
/>
</SplitItem>
<SplitItem id={`${className}-select-menu`}>
<Select
<KeycloakSelect
variant={SelectVariant.single}
aria-label={t("unitLabel")}
className={`${className}-select`}
onSelect={(_, value) => {
onSelect={(value) => {
setMultiplier(value as number);
updateTimeout(timeValue, value as number);
setOpen(false);
}}
menuAppendTo={menuAppendTo}
selections={[multiplier]}
selections={multiplier}
onToggle={() => {
setOpen(!open);
}}
@ -157,7 +157,7 @@ export const TimeSelector = ({
{t(time.label)}
</SelectOption>
))}
</Select>
</KeycloakSelect>
</SplitItem>
</Split>
);

View file

@ -7,25 +7,22 @@ import {
ButtonVariant,
InputGroup,
InputGroupItem,
SelectOption,
Text,
TextContent,
TextInput,
TextVariants,
} from "@patternfly/react-core";
import {
Select,
SelectOption,
SelectVariant,
} from "@patternfly/react-core/deprecated";
import { CheckIcon } from "@patternfly/react-icons";
import { ReactNode, useState } from "react";
import { useForm } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { Form } from "react-router-dom";
import { label } from "@keycloak/keycloak-ui-shared";
import { SelectVariant, label } from "@keycloak/keycloak-ui-shared";
import { useAlerts } from "../alert/Alerts";
import { UserAttribute } from "./UserDataTable";
import { KeycloakSelect } from "../select/KeycloakSelect";
type UserDataTableAttributeSearchFormProps = {
activeFilters: UserAttribute[];
@ -132,12 +129,12 @@ export function UserDataTableAttributeSearchForm({
const createAttributeKeyInputField = () => {
if (profile) {
return (
<Select
<KeycloakSelect
data-testid="search-attribute-name"
variant={SelectVariant.typeahead}
onToggle={(_event, isOpen) => setSelectAttributeKeyOpen(isOpen)}
onToggle={(isOpen) => setSelectAttributeKeyOpen(isOpen)}
selections={getValues().displayName}
onSelect={(_, selectedValue) => {
onSelect={(selectedValue) => {
setValue("displayName", selectedValue.toString());
if (isAttributeKeyDuplicate()) {
setError("name", { type: "conflict" });
@ -165,7 +162,7 @@ export function UserDataTableAttributeSearchForm({
}}
/>
))}
</Select>
</KeycloakSelect>
);
} else {
return (

View file

@ -7,13 +7,12 @@ import {
SearchInput,
ToolbarItem,
InputGroupItem,
} from "@patternfly/react-core";
import {
Dropdown,
MenuToggle,
DropdownList,
DropdownItem,
KebabToggle,
} from "@patternfly/react-core/deprecated";
import { ArrowRightIcon } from "@patternfly/react-icons";
} from "@patternfly/react-core";
import { ArrowRightIcon, EllipsisVIcon } from "@patternfly/react-icons";
import { ReactNode, useState } from "react";
import { useTranslation } from "react-i18next";
@ -170,10 +169,20 @@ export function UserDataTableToolbarItems({
) : (
<ToolbarItem>
<Dropdown
toggle={<KebabToggle onToggle={(_event, open) => setKebabOpen(open)} />}
toggle={(ref) => (
<MenuToggle
ref={ref}
isExpanded={kebabOpen}
variant="plain"
onClick={() => setKebabOpen(!kebabOpen)}
>
<EllipsisVIcon />
</MenuToggle>
)}
isOpen={kebabOpen}
isPlain
dropdownItems={[
shouldFocusToggleOnSelect
>
<DropdownList>
<DropdownItem
key="deleteUser"
component="button"
@ -184,7 +193,7 @@ export function UserDataTableToolbarItems({
}}
>
{t("deleteUser")}
</DropdownItem>,
</DropdownItem>
<DropdownItem
key="unlock"
@ -195,9 +204,9 @@ export function UserDataTableToolbarItems({
}}
>
{t("unlockAllUsers")}
</DropdownItem>,
]}
/>
</DropdownItem>
</DropdownList>
</Dropdown>
</ToolbarItem>
);

View file

@ -1,13 +1,20 @@
import type UserRepresentation from "@keycloak/keycloak-admin-client/lib/defs/userRepresentation";
import type { UserQuery } from "@keycloak/keycloak-admin-client/lib/resources/users";
import { FormGroup } from "@patternfly/react-core";
import {
Button,
Chip,
ChipGroup,
FormGroup,
MenuToggle,
Select,
SelectList,
SelectOption,
SelectVariant,
} from "@patternfly/react-core/deprecated";
TextInputGroup,
TextInputGroupMain,
TextInputGroupUtilities,
} from "@patternfly/react-core";
import { debounce } from "lodash-es";
import { useCallback, useState } from "react";
import { useCallback, useRef, useState } from "react";
import { Controller, useFormContext } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { FormErrorText, HelpItem } from "@keycloak/keycloak-ui-shared";
@ -15,9 +22,12 @@ import { useAdminClient } from "../../admin-client";
import { useFetch } from "../../utils/useFetch";
import useToggle from "../../utils/useToggle";
import type { ComponentProps } from "../dynamic/components";
import { TimesIcon } from "@patternfly/react-icons";
type UserSelectVariant = "typeaheadMulti" | "typeahead";
type UserSelectProps = ComponentProps & {
variant?: SelectVariant;
variant?: UserSelectVariant;
isRequired?: boolean;
};
@ -27,7 +37,7 @@ export const UserSelect = ({
helpText,
defaultValue,
isRequired,
variant = SelectVariant.typeaheadMulti,
variant = "typeaheadMulti",
}: UserSelectProps) => {
const { adminClient } = useAdminClient();
@ -39,9 +49,11 @@ export const UserSelect = ({
} = useFormContext();
const values: string[] | undefined = getValues(name!);
const [open, toggleOpen] = useToggle();
const [open, toggleOpen, setOpen] = useToggle();
const [users, setUsers] = useState<(UserRepresentation | undefined)[]>([]);
const [inputValue, setInputValue] = useState("");
const [search, setSearch] = useState("");
const textInputRef = useRef<HTMLInputElement>();
const debounceFn = useCallback(debounce(setSearch, 1000), []);
@ -90,28 +102,97 @@ export const UserSelect = ({
defaultValue={defaultValue}
control={control}
rules={
isRequired && variant === SelectVariant.typeaheadMulti
isRequired && variant === "typeaheadMulti"
? { validate: (value) => value.length > 0 }
: { required: isRequired }
}
render={({ field }) => (
<Select
toggleId={name!}
variant={variant}
placeholderText={t("selectAUser")}
onToggle={toggleOpen}
id={name!}
toggle={(ref) => (
<MenuToggle
data-testid={name!}
ref={ref}
variant="typeahead"
onClick={toggleOpen}
isExpanded={open}
isFullWidth
status={errors[name!] ? "danger" : undefined}
>
<TextInputGroup isPlain>
<TextInputGroupMain
value={inputValue}
onClick={toggleOpen}
onChange={(_, value) => {
setOpen(true);
setInputValue(value);
debounceFn(value);
}}
autoComplete="off"
innerRef={textInputRef}
placeholderText={t("selectAUser")}
{...(field.value && {
"aria-activedescendant": field.value,
})}
role="combobox"
isExpanded={open}
aria-controls="select-create-typeahead-listbox"
>
{variant === "typeaheadMulti" &&
Array.isArray(field.value) && (
<ChipGroup aria-label="Current selections">
{field.value.map(
(selection: string, index: number) => (
<Chip
key={index}
onClick={(ev) => {
ev.stopPropagation();
field.onChange(
field.value.filter(
(item: string) => item !== selection,
),
);
}}
>
{selection}
</Chip>
),
)}
</ChipGroup>
)}
</TextInputGroupMain>
<TextInputGroupUtilities>
{!!search && (
<Button
variant="plain"
onClick={() => {
setInputValue("");
setSearch("");
field.onChange([]);
textInputRef?.current?.focus();
}}
aria-label="Clear input value"
>
<TimesIcon aria-hidden />
</Button>
)}
</TextInputGroupUtilities>
</TextInputGroup>
</MenuToggle>
)}
isOpen={open}
selections={field.value}
onFilter={(_, value) => {
debounceFn(value);
return convert(users);
}}
selected={field.value}
onSelect={(_, v) => {
const option = v.toString();
if (variant !== SelectVariant.typeaheadMulti) {
field.value.includes(option)
? field.onChange([])
: field.onChange([option]);
const option = v?.toString();
if (variant !== "typeaheadMulti") {
const removed = field.value.includes(option);
removed ? field.onChange([]) : field.onChange([option]);
setInputValue(
removed
? ""
: users.find((u) => u?.id === option)?.username || "",
);
setOpen(false);
} else {
const changedValue = field.value.find(
(v: string) => v === option,
@ -120,11 +201,10 @@ export const UserSelect = ({
: [...field.value, option];
field.onChange(changedValue);
}
toggleOpen();
}}
aria-label={t(name!)}
>
{convert(users)}
<SelectList>{convert(users)}</SelectList>
</Select>
)}
/>

View file

@ -2,8 +2,11 @@ import {
Badge,
Button,
Divider,
Dropdown,
DropdownList,
Level,
LevelItem,
MenuToggle,
PageSection,
Switch,
Text,
@ -12,11 +15,6 @@ import {
ToolbarContent,
ToolbarItem,
} from "@patternfly/react-core";
import {
Dropdown,
DropdownPosition,
DropdownToggle,
} from "@patternfly/react-core/deprecated";
import {
ReactElement,
ReactNode,
@ -151,20 +149,24 @@ export const ViewHeader = ({
{dropdownItems && (
<ToolbarItem>
<Dropdown
position={DropdownPosition.right}
toggle={
<DropdownToggle
popperProps={{
position: "right",
}}
toggle={(ref) => (
<MenuToggle
ref={ref}
isDisabled={isDropdownDisabled}
id={actionsDropdownId}
onToggle={onDropdownToggle}
onClick={onDropdownToggle}
data-testid="action-dropdown"
>
{t("action")}
</DropdownToggle>
}
</MenuToggle>
)}
isOpen={isDropdownOpen}
dropdownItems={dropdownItems}
data-testid="action-dropdown"
/>
>
<DropdownList>{dropdownItems}</DropdownList>
</Dropdown>
</ToolbarItem>
)}
</ToolbarContent>
@ -193,18 +195,20 @@ export const ViewHeader = ({
{lowerDropdownItems && (
<Dropdown
className="keycloak__user-federation__dropdown"
toggle={
<DropdownToggle
onToggle={() => onLowerDropdownToggle()}
toggleVariant="primary"
toggle={(ref) => (
<MenuToggle
ref={ref}
onClick={onLowerDropdownToggle}
variant="primary"
id="ufToggleId"
>
{t(lowerDropdownMenuTitle)}
</DropdownToggle>
}
</MenuToggle>
)}
isOpen={isLowerDropdownOpen}
dropdownItems={lowerDropdownItems}
/>
>
<DropdownList>{lowerDropdownItems}</DropdownList>
</Dropdown>
)}
{lowerButton && (
<Button

View file

@ -1,4 +1,5 @@
import type AdminEventRepresentation from "@keycloak/keycloak-admin-client/lib/defs/adminEventRepresentation";
import { TextControl } from "@keycloak/keycloak-ui-shared";
import { CodeEditor, Language } from "@patternfly/react-code-editor";
import {
ActionGroup,
@ -12,12 +13,8 @@ import {
FormGroup,
Modal,
ModalVariant,
} from "@patternfly/react-core";
import {
Select,
SelectOption,
SelectVariant,
} from "@patternfly/react-core/deprecated";
} from "@patternfly/react-core";
import {
Table,
TableVariant,
@ -32,10 +29,13 @@ import { pickBy } from "lodash-es";
import { PropsWithChildren, useMemo, useState } from "react";
import { Controller, FormProvider, useForm } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { TextControl } from "@keycloak/keycloak-ui-shared";
import { useAdminClient } from "../admin-client";
import DropdownPanel from "../components/dropdown-panel/DropdownPanel";
import { ListEmptyState } from "../components/list-empty-state/ListEmptyState";
import {
KeycloakSelect,
SelectVariant,
} from "../components/select/KeycloakSelect";
import {
Action,
KeycloakDataTable,
@ -285,9 +285,8 @@ export const AdminEvents = () => {
name="resourceTypes"
control={control}
render={({ field }) => (
<Select
<KeycloakSelect
className="keycloak__events_search__type_select"
name="resourceTypes"
data-testid="resource-types-searchField"
chipGroupProps={{
numChips: 1,
@ -296,11 +295,11 @@ export const AdminEvents = () => {
}}
variant={SelectVariant.typeaheadMulti}
typeAheadAriaLabel="Select"
onToggle={(_event, isOpen) =>
onToggle={(isOpen) =>
setSelectResourceTypesOpen(isOpen)
}
selections={field.value}
onSelect={(_, selectedValue) => {
onSelect={(selectedValue) => {
const option = selectedValue.toString();
const changedValue = field.value.includes(option)
? field.value.filter((item) => item !== option)
@ -308,8 +307,7 @@ export const AdminEvents = () => {
field.onChange(changedValue);
}}
onClear={(resource) => {
resource.stopPropagation();
onClear={() => {
field.onChange([]);
}}
isOpen={selectResourceTypesOpen}
@ -337,7 +335,7 @@ export const AdminEvents = () => {
{resourceTypes?.map((option) => (
<SelectOption key={option} value={option} />
))}
</Select>
</KeycloakSelect>
)}
/>
</FormGroup>
@ -350,9 +348,8 @@ export const AdminEvents = () => {
name="operationTypes"
control={control}
render={({ field }) => (
<Select
<KeycloakSelect
className="keycloak__events_search__type_select"
name="operationTypes"
data-testid="operation-types-searchField"
chipGroupProps={{
numChips: 1,
@ -361,11 +358,11 @@ export const AdminEvents = () => {
}}
variant={SelectVariant.typeaheadMulti}
typeAheadAriaLabel="Select"
onToggle={(_event, isOpen) =>
onToggle={(isOpen) =>
setSelectOperationTypesOpen(isOpen)
}
selections={field.value}
onSelect={(_, selectedValue) => {
onSelect={(selectedValue) => {
const option = selectedValue.toString();
const changedValue = field.value.includes(option)
? field.value.filter((item) => item !== option)
@ -373,8 +370,7 @@ export const AdminEvents = () => {
field.onChange(changedValue);
}}
onClear={(operation) => {
operation.stopPropagation();
onClear={() => {
field.onChange([]);
}}
isOpen={selectOperationTypesOpen}
@ -400,9 +396,12 @@ export const AdminEvents = () => {
}
>
{operationTypes?.map((option) => (
<SelectOption key={option} value={option} />
<SelectOption
key={option.toString()}
value={option}
/>
))}
</Select>
</KeycloakSelect>
)}
/>
</FormGroup>

View file

@ -1,6 +1,7 @@
import type EventRepresentation from "@keycloak/keycloak-admin-client/lib/defs/eventRepresentation";
import type EventType from "@keycloak/keycloak-admin-client/lib/defs/eventTypes";
import type { RealmEventsConfigRepresentation } from "@keycloak/keycloak-admin-client/lib/defs/realmEventsConfigRepresentation";
import { TextControl } from "@keycloak/keycloak-ui-shared";
import {
ActionGroup,
Button,
@ -17,15 +18,11 @@ import {
FormGroup,
Icon,
PageSection,
SelectOption,
Tab,
TabTitleText,
Tooltip,
} from "@patternfly/react-core";
import {
Select,
SelectOption,
SelectVariant,
} from "@patternfly/react-core/deprecated";
import { CheckCircleIcon, WarningTriangleIcon } from "@patternfly/react-icons";
import { cellWidth, expandable } from "@patternfly/react-table";
import { pickBy } from "lodash-es";
@ -33,7 +30,6 @@ import { useState } from "react";
import { Controller, FormProvider, useForm } from "react-hook-form";
import { Trans, useTranslation } from "react-i18next";
import { Link } from "react-router-dom";
import { TextControl } from "@keycloak/keycloak-ui-shared";
import { useAdminClient } from "../admin-client";
import DropdownPanel from "../components/dropdown-panel/DropdownPanel";
import { ListEmptyState } from "../components/list-empty-state/ListEmptyState";
@ -41,6 +37,10 @@ import {
RoutableTabs,
useRoutableTab,
} from "../components/routable-tabs/RoutableTabs";
import {
KeycloakSelect,
SelectVariant,
} from "../components/select/KeycloakSelect";
import { KeycloakDataTable } from "../components/table-toolbar/KeycloakDataTable";
import { ViewHeader } from "../components/view-header/ViewHeader";
import { useRealm } from "../context/realm-context/RealmContext";
@ -266,9 +266,8 @@ export default function EventsSection() {
name="type"
control={control}
render={({ field }) => (
<Select
<KeycloakSelect
className="keycloak__events_search__type_select"
name="eventType"
data-testid="event-type-searchField"
chipGroupProps={{
numChips: 1,
@ -277,9 +276,9 @@ export default function EventsSection() {
}}
variant={SelectVariant.typeaheadMulti}
typeAheadAriaLabel="Select"
onToggle={(_event, isOpen) => setSelectOpen(isOpen)}
onToggle={(isOpen) => setSelectOpen(isOpen)}
selections={field.value}
onSelect={(_, selectedValue) => {
onSelect={(selectedValue) => {
const option = selectedValue.toString() as EventType;
const changedValue = field.value.includes(option)
? field.value.filter((item) => item !== option)
@ -287,8 +286,7 @@ export default function EventsSection() {
field.onChange(changedValue);
}}
onClear={(event) => {
event.stopPropagation();
onClear={() => {
field.onChange([]);
}}
isOpen={selectOpen}
@ -316,7 +314,7 @@ export default function EventsSection() {
{t(`eventTypes.${option}.name`)}
</SelectOption>
))}
</Select>
</KeycloakSelect>
)}
/>
</FormGroup>

View file

@ -15,7 +15,7 @@
line-height: 0px;
}
.keycloak__events_search__type_select .pf-v5-c-select__menu {
.keycloak__events_search__type_select .pf-v5-c-menu__list {
max-height: 200px;
overflow: auto;
}

View file

@ -6,6 +6,7 @@ import {
DrawerContentBody,
DrawerHead,
DrawerPanelContent,
DropdownItem,
PageSection,
PageSectionVariants,
Tab,
@ -13,7 +14,6 @@ import {
Tabs,
Tooltip,
} from "@patternfly/react-core";
import { DropdownItem } from "@patternfly/react-core/deprecated";
import { AngleLeftIcon, TreeIcon } from "@patternfly/react-icons";
import { useState } from "react";
import { useTranslation } from "react-i18next";

View file

@ -1,12 +1,16 @@
import type GroupRepresentation from "@keycloak/keycloak-admin-client/lib/defs/groupRepresentation";
import type UserRepresentation from "@keycloak/keycloak-admin-client/lib/defs/userRepresentation";
import { SubGroupQuery } from "@keycloak/keycloak-admin-client/lib/resources/groups";
import { Button, Checkbox, ToolbarItem } from "@patternfly/react-core";
import {
Button,
Checkbox,
Dropdown,
DropdownItem,
KebabToggle,
} from "@patternfly/react-core/deprecated";
DropdownList,
MenuToggle,
ToolbarItem,
} from "@patternfly/react-core";
import { EllipsisVIcon } from "@patternfly/react-icons";
import { uniqBy } from "lodash-es";
import { useState } from "react";
import { useTranslation } from "react-i18next";
@ -202,15 +206,22 @@ export const Members = () => {
</ToolbarItem>
<ToolbarItem>
<Dropdown
toggle={
<KebabToggle
onToggle={() => setIsKebabOpen(!isKebabOpen)}
toggle={(ref) => (
<MenuToggle
ref={ref}
variant="plain"
onClick={() => setIsKebabOpen(!isKebabOpen)}
isExpanded={isKebabOpen}
isDisabled={selectedRows.length === 0}
/>
}
aria-label="Actions"
>
<EllipsisVIcon />
</MenuToggle>
)}
shouldFocusToggleOnSelect
isOpen={isKebabOpen}
isPlain
dropdownItems={[
>
<DropdownList>
<DropdownItem
key="action"
component="button"
@ -236,9 +247,9 @@ export const Members = () => {
}}
>
{t("leave")}
</DropdownItem>,
]}
/>
</DropdownItem>
</DropdownList>
</Dropdown>
</ToolbarItem>
</>
)

View file

@ -1,14 +1,16 @@
import { useTranslation } from "react-i18next";
import { Button, ToolbarItem } from "@patternfly/react-core";
import {
Button,
Dropdown,
DropdownItem,
KebabToggle,
} from "@patternfly/react-core/deprecated";
import { useSubGroups } from "../SubGroupsContext";
DropdownList,
MenuToggle,
ToolbarItem,
} from "@patternfly/react-core";
import { EllipsisVIcon } from "@patternfly/react-icons";
import { useTranslation } from "react-i18next";
import { useAccess } from "../../context/access/Access";
import useToggle from "../../utils/useToggle";
import { useSubGroups } from "../SubGroupsContext";
type GroupToolbarProps = {
toggleCreate: () => void;
@ -43,12 +45,21 @@ export const GroupToolbar = ({
</ToolbarItem>
<ToolbarItem>
<Dropdown
toggle={
<KebabToggle onToggle={toggleKebab} isDisabled={kebabDisabled} />
}
toggle={(ref) => (
<MenuToggle
ref={ref}
isExpanded={openKebab}
onClick={toggleKebab}
isDisabled={kebabDisabled}
variant="plain"
aria-label="Actions"
>
<EllipsisVIcon />
</MenuToggle>
)}
isOpen={openKebab}
isPlain
dropdownItems={[
>
<DropdownList>
<DropdownItem
key="action"
component="button"
@ -58,9 +69,9 @@ export const GroupToolbar = ({
}}
>
{t("delete")}
</DropdownItem>,
]}
/>
</DropdownItem>
</DropdownList>
</Dropdown>
</ToolbarItem>
</>
);

View file

@ -8,15 +8,14 @@ import {
Tooltip,
TreeView,
TreeViewDataItem,
} from "@patternfly/react-core";
import {
Dropdown,
MenuToggle,
DropdownList,
Divider,
DropdownItem,
DropdownPosition,
DropdownSeparator,
KebabToggle,
} from "@patternfly/react-core/deprecated";
import { AngleRightIcon } from "@patternfly/react-icons";
} from "@patternfly/react-core";
import { AngleRightIcon, EllipsisVIcon } from "@patternfly/react-icons";
import { unionBy } from "lodash-es";
import { useRef, useState } from "react";
import { useTranslation } from "react-i18next";
@ -104,26 +103,38 @@ const GroupTreeContextMenu = ({
}}
/>
<Dropdown
toggle={<KebabToggle onToggle={toggleOpen} />}
popperProps={{
position: "right",
}}
toggle={(ref) => (
<MenuToggle
ref={ref}
onClick={toggleOpen}
isExpanded={isOpen}
variant="plain"
aria-label="Actions"
>
<EllipsisVIcon />
</MenuToggle>
)}
isOpen={isOpen}
isPlain
position={DropdownPosition.right}
dropdownItems={[
>
<DropdownList>
<DropdownItem key="rename" onClick={toggleRenameOpen}>
{t("rename")}
</DropdownItem>,
</DropdownItem>
<DropdownItem key="move" onClick={toggleMoveOpen}>
{t("moveTo")}
</DropdownItem>,
</DropdownItem>
<DropdownItem key="create" onClick={toggleCreateOpen}>
{t("createChildGroup")}
</DropdownItem>,
<DropdownSeparator key="separator" />,
</DropdownItem>
<Divider key="separator" />,
<DropdownItem key="delete" onClick={toggleDeleteOpen}>
{t("delete")}
</DropdownItem>,
]}
/>
</DropdownItem>
</DropdownList>
</Dropdown>
</>
);
};

View file

@ -7,7 +7,12 @@ import {
Button,
ButtonVariant,
CardTitle,
Dropdown,
DropdownGroup,
DropdownItem,
DropdownList,
Gallery,
MenuToggle,
PageSection,
Split,
SplitItem,
@ -16,12 +21,6 @@ import {
TextVariants,
ToolbarItem,
} from "@patternfly/react-core";
import {
Dropdown,
DropdownGroup,
DropdownItem,
DropdownToggle,
} from "@patternfly/react-core/deprecated";
import { IFormatterValueType } from "@patternfly/react-table";
import { groupBy, sortBy } from "lodash-es";
import { Fragment, useState } from "react";
@ -128,18 +127,19 @@ export default function IdentityProvidersSection() {
<DropdownItem
key={provider.id}
value={provider.id}
component={
<Link
to={toIdentityProviderCreate({
component="a"
data-testid={provider.id}
onClick={() =>
navigate(
toIdentityProviderCreate({
realm,
providerId: provider.id,
})}
data-testid={provider.id}
>
{provider.name}
</Link>
}),
)
}
/>
>
{provider.name}
</DropdownItem>
))}
</DropdownGroup>
));
@ -229,17 +229,19 @@ export default function IdentityProvidersSection() {
<ToolbarItem>
<Dropdown
data-testid="addProviderDropdown"
toggle={
<DropdownToggle
onToggle={() => setAddProviderOpen(!addProviderOpen)}
toggleVariant="primary"
toggle={(ref) => (
<MenuToggle
ref={ref}
onClick={() => setAddProviderOpen(!addProviderOpen)}
variant="primary"
>
{t("addProvider")}
</DropdownToggle>
}
</MenuToggle>
)}
isOpen={addProviderOpen}
dropdownItems={identityProviderOptions()}
/>
>
<DropdownList>{identityProviderOptions()}</DropdownList>
</Dropdown>
</ToolbarItem>
<ToolbarItem>

View file

@ -6,9 +6,9 @@ import {
AlertVariant,
Button,
ButtonVariant,
DropdownItem,
PageSection,
} from "@patternfly/react-core";
import { DropdownItem } from "@patternfly/react-core/deprecated";
import { useState } from "react";
import { FormProvider, useForm } from "react-hook-form";
import { useTranslation } from "react-i18next";

View file

@ -1,19 +1,18 @@
import type IdentityProviderMapperRepresentation from "@keycloak/keycloak-admin-client/lib/defs/identityProviderMapperRepresentation";
import type { IdentityProviderMapperTypeRepresentation } from "@keycloak/keycloak-admin-client/lib/defs/identityProviderMapperTypeRepresentation";
import { FormGroup } from "@patternfly/react-core";
import {
Select,
SelectOption,
SelectVariant,
} from "@patternfly/react-core/deprecated";
import { useState } from "react";
import { Controller, UseFormReturn } from "react-hook-form";
import { useTranslation } from "react-i18next";
import {
HelpItem,
SelectControl,
TextControl,
} from "@keycloak/keycloak-ui-shared";
import { FormGroup, SelectOption } from "@patternfly/react-core";
import { useState } from "react";
import { Controller, UseFormReturn } from "react-hook-form";
import { useTranslation } from "react-i18next";
import {
KeycloakSelect,
SelectVariant,
} from "../../components/select/KeycloakSelect";
import type { IdPMapperRepresentationWithAttributes } from "./AddMapper";
type AddMapperFormProps = {
@ -74,13 +73,12 @@ export const AddMapperForm = ({
defaultValue={mapperTypes[0].id}
control={control}
render={({ field }) => (
<Select
<KeycloakSelect
toggleId="identityProviderMapper"
data-testid="idp-mapper-select"
isDisabled={!!id}
required
onToggle={() => setMapperTypeOpen(!mapperTypeOpen)}
onSelect={(_, value) => {
onSelect={(value) => {
const mapperType =
value as IdentityProviderMapperTypeRepresentation;
updateMapperType(mapperType);
@ -102,7 +100,7 @@ export const AddMapperForm = ({
{option.name}
</SelectOption>
))}
</Select>
</KeycloakSelect>
)}
/>
</FormGroup>

View file

@ -1,25 +1,25 @@
import type AuthenticationFlowRepresentation from "@keycloak/keycloak-admin-client/lib/defs/authenticationFlowRepresentation";
import type IdentityProviderRepresentation from "@keycloak/keycloak-admin-client/lib/defs/identityProviderRepresentation";
import {
FormGroup,
Switch,
TextInput,
ValidatedOptions,
} from "@patternfly/react-core";
import {
Select,
SelectOption,
SelectVariant,
} from "@patternfly/react-core/deprecated";
import { useState } from "react";
import { Controller, useFormContext, useWatch } from "react-hook-form";
import { useTranslation } from "react-i18next";
import {
FormErrorText,
HelpItem,
SelectControl,
} from "@keycloak/keycloak-ui-shared";
import {
FormGroup,
SelectOption,
Switch,
TextInput,
ValidatedOptions,
} from "@patternfly/react-core";
import { useState } from "react";
import { Controller, useFormContext, useWatch } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { useAdminClient } from "../../admin-client";
import {
KeycloakSelect,
SelectVariant,
} from "../../components/select/KeycloakSelect";
import { useFetch } from "../../utils/useFetch";
import useIsFeatureEnabled, { Feature } from "../../utils/useIsFeatureEnabled";
import type { FieldProps } from "../component/FormGroupField";
@ -59,11 +59,10 @@ const LoginFlow = ({
defaultValue={defaultValue}
control={control}
render={({ field }) => (
<Select
<KeycloakSelect
toggleId={label}
required
onToggle={() => setOpen(!open)}
onSelect={(_, value) => {
onSelect={(value) => {
field.onChange(value as string);
setOpen(false);
}}
@ -90,7 +89,7 @@ const LoginFlow = ({
</SelectOption>
)) || []),
]}
</Select>
</KeycloakSelect>
)}
/>
</FormGroup>

View file

@ -5,16 +5,13 @@ import {
Button,
ButtonVariant,
Divider,
DropdownItem,
Form,
PageSection,
Tab,
TabTitleText,
ToolbarItem,
} from "@patternfly/react-core";
import {
DropdownItem,
DropdownSeparator,
} from "@patternfly/react-core/deprecated";
import { useMemo, useState } from "react";
import {
Controller,
@ -209,7 +206,7 @@ const Header = ({ onChange, value, save, toggleDeleteDialog }: HeaderProps) => {
</DropdownItem>,
]
: []),
<DropdownSeparator key="separator" />,
<Divider key="separator" />,
<DropdownItem key="delete" onClick={() => toggleDeleteDialog()}>
{t("delete")}
</DropdownItem>,

View file

@ -3,17 +3,14 @@ import {
Form,
FormGroup,
NumberInput,
} from "@patternfly/react-core";
import {
Select,
SelectOption,
SelectVariant,
} from "@patternfly/react-core/deprecated";
} from "@patternfly/react-core";
import { useState } from "react";
import { Controller, useFormContext } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { HelpItem } from "@keycloak/keycloak-ui-shared";
import { HelpItem, SelectVariant } from "@keycloak/keycloak-ui-shared";
import { KeycloakSelect } from "../../components/select/KeycloakSelect";
import { FormGroupField } from "../component/FormGroupField";
import { SwitchField } from "../component/SwitchField";
import { TextField } from "../component/TextField";
@ -65,11 +62,10 @@ export const ExtendedNonDiscoverySettings = () => {
defaultValue=""
control={control}
render={({ field }) => (
<Select
<KeycloakSelect
toggleId="prompt"
required
onToggle={() => setPromptOpen(!promptOpen)}
onSelect={(_, value) => {
onSelect={(value) => {
field.onChange(value as string);
setPromptOpen(false);
}}
@ -87,7 +83,7 @@ export const ExtendedNonDiscoverySettings = () => {
{t(`prompts.${key}`)}
</SelectOption>
))}
</Select>
</KeycloakSelect>
)}
/>
</FormGroupField>

View file

@ -1,9 +1,4 @@
import { FormGroup } from "@patternfly/react-core";
import {
Select,
SelectOption,
SelectVariant,
} from "@patternfly/react-core/deprecated";
import { FormGroup, SelectOption } from "@patternfly/react-core";
import { useState } from "react";
import { Controller, useFormContext, useWatch } from "react-hook-form";
import { useTranslation } from "react-i18next";
@ -14,6 +9,10 @@ import { SwitchField } from "../component/SwitchField";
import { sortProviders } from "../../util";
import { useServerInfo } from "../../context/server-info/ServerInfoProvider";
import { TextField } from "../component/TextField";
import {
KeycloakSelect,
SelectVariant,
} from "../../components/select/KeycloakSelect";
const clientAuthentications = [
"client_secret_post",
@ -52,11 +51,10 @@ export const OIDCAuthentication = ({ create = true }: { create?: boolean }) => {
defaultValue={clientAuthentications[0]}
control={control}
render={({ field }) => (
<Select
<KeycloakSelect
toggleId="clientAuthentication"
required
onToggle={() => setOpenClientAuth(!openClientAuth)}
onSelect={(_, value) => {
onSelect={(value) => {
field.onChange(value as string);
setOpenClientAuth(false);
}}
@ -74,7 +72,7 @@ export const OIDCAuthentication = ({ create = true }: { create?: boolean }) => {
{t(`clientAuthentications.${option}`)}
</SelectOption>
))}
</Select>
</KeycloakSelect>
)}
/>
</FormGroup>
@ -97,11 +95,11 @@ export const OIDCAuthentication = ({ create = true }: { create?: boolean }) => {
defaultValue=""
control={control}
render={({ field }) => (
<Select
<KeycloakSelect
maxHeight={200}
toggleId="clientAssertionSigningAlg"
onToggle={() => setOpenClientAuthSigAlg(!openClientAuthSigAlg)}
onSelect={(_, value) => {
onSelect={(value) => {
field.onChange(value.toString());
setOpenClientAuthSigAlg(false);
}}
@ -119,10 +117,12 @@ export const OIDCAuthentication = ({ create = true }: { create?: boolean }) => {
selected={option === field.value}
key={option}
value={option}
/>
>
{option}
</SelectOption>
)),
]}
</Select>
</KeycloakSelect>
)}
/>
</FormGroup>

View file

@ -1,15 +1,11 @@
import { FormGroup } from "@patternfly/react-core";
import {
Select,
SelectOption,
SelectVariant,
} from "@patternfly/react-core/deprecated";
import { FormGroup, SelectOption } from "@patternfly/react-core";
import { useState } from "react";
import { Controller, useFormContext } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { HelpItem } from "@keycloak/keycloak-ui-shared";
import { HelpItem, SelectVariant } from "@keycloak/keycloak-ui-shared";
import { MultiLineInput } from "../../components/multi-line-input/MultiLineInput";
import { KeycloakSelect } from "../../components/select/KeycloakSelect";
const comparisonValues = ["exact", "minimum", "maximum", "better"];
@ -31,12 +27,11 @@ export const ReqAuthnConstraints = () => {
defaultValue={comparisonValues[0]}
control={control}
render={({ field }) => (
<Select
<KeycloakSelect
toggleId="comparison"
required
direction="up"
onToggle={(_event, isExpanded) => setComparisonOpen(isExpanded)}
onSelect={(_, value) => {
onToggle={(isExpanded) => setComparisonOpen(isExpanded)}
onSelect={(value) => {
field.onChange(value.toString());
setComparisonOpen(false);
}}
@ -54,7 +49,7 @@ export const ReqAuthnConstraints = () => {
{t(option)}
</SelectOption>
))}
</Select>
</KeycloakSelect>
)}
/>
</FormGroup>

View file

@ -1,5 +1,4 @@
import { ButtonVariant } from "@patternfly/react-core";
import { DropdownItem } from "@patternfly/react-core/deprecated";
import { ButtonVariant, DropdownItem } from "@patternfly/react-core";
import { useTranslation } from "react-i18next";
import { useNavigate, useParams } from "react-router-dom";
import { useAdminClient } from "../admin-client";

View file

@ -2,11 +2,11 @@ import type RoleRepresentation from "@keycloak/keycloak-admin-client/lib/defs/ro
import {
AlertVariant,
ButtonVariant,
DropdownItem,
PageSection,
Tab,
TabTitleText,
} from "@patternfly/react-core";
import { DropdownItem } from "@patternfly/react-core/deprecated";
import { useState } from "react";
import {
FormProvider,

Some files were not shown because too many files have changed in this diff Show more