Realm settings(localization): add locale dropdown to table (#936)
* add localization dropdown uncomment isDisabled clean up names comment out localization tab again disable actions menu if form not submitted uncomment tab use currentRealm for tableLoader fix axe issues labeled as serious PR feedback from Erik use isSearching instead of keepToolbar * fix node build * use object entries Co-authored-by: Jon Koops <jonkoops@gmail.com>
This commit is contained in:
parent
c575ffa491
commit
f93d478f44
4 changed files with 97 additions and 21 deletions
|
@ -7,6 +7,7 @@ import {
|
||||||
NavGroup,
|
NavGroup,
|
||||||
NavList,
|
NavList,
|
||||||
PageSidebar,
|
PageSidebar,
|
||||||
|
Divider,
|
||||||
} from "@patternfly/react-core";
|
} from "@patternfly/react-core";
|
||||||
|
|
||||||
import { RealmSelector } from "./components/realm-selector/RealmSelector";
|
import { RealmSelector } from "./components/realm-selector/RealmSelector";
|
||||||
|
@ -90,13 +91,9 @@ export const PageNav: React.FunctionComponent = () => {
|
||||||
<RealmSelector />
|
<RealmSelector />
|
||||||
</NavItem>
|
</NavItem>
|
||||||
</NavList>
|
</NavList>
|
||||||
{!isOnAddRealm && (
|
<Divider />
|
||||||
<NavGroup title="">
|
|
||||||
<LeftNav title="home" path="/" />
|
|
||||||
</NavGroup>
|
|
||||||
)}
|
|
||||||
{showManage && !isOnAddRealm && (
|
{showManage && !isOnAddRealm && (
|
||||||
<NavGroup title={t("manage")}>
|
<NavGroup aria-label={t("manage")} title={t("manage")}>
|
||||||
<LeftNav title="clients" path="/clients" />
|
<LeftNav title="clients" path="/clients" />
|
||||||
<LeftNav title="clientScopes" path="/client-scopes" />
|
<LeftNav title="clientScopes" path="/client-scopes" />
|
||||||
<LeftNav title="realmRoles" path="/roles" />
|
<LeftNav title="realmRoles" path="/roles" />
|
||||||
|
@ -108,7 +105,7 @@ export const PageNav: React.FunctionComponent = () => {
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{showConfigure && !isOnAddRealm && (
|
{showConfigure && !isOnAddRealm && (
|
||||||
<NavGroup title={t("configure")}>
|
<NavGroup aria-label={t("configure")} title={t("configure")}>
|
||||||
<LeftNav title="realmSettings" path="/realm-settings" />
|
<LeftNav title="realmSettings" path="/realm-settings" />
|
||||||
<LeftNav title="authentication" path="/authentication" />
|
<LeftNav title="authentication" path="/authentication" />
|
||||||
<LeftNav title="identityProviders" path="/identity-providers" />
|
<LeftNav title="identityProviders" path="/identity-providers" />
|
||||||
|
|
|
@ -375,6 +375,7 @@ export function KeycloakDataTable<T>({
|
||||||
{((data && data.length > 0) ||
|
{((data && data.length > 0) ||
|
||||||
search !== "" ||
|
search !== "" ||
|
||||||
isSearching ||
|
isSearching ||
|
||||||
|
emptyState ||
|
||||||
loading) && (
|
loading) && (
|
||||||
<PaginatingTableToolbar
|
<PaginatingTableToolbar
|
||||||
count={data?.length || 0}
|
count={data?.length || 0}
|
||||||
|
@ -413,7 +414,8 @@ export function KeycloakDataTable<T>({
|
||||||
{!loading &&
|
{!loading &&
|
||||||
(!data || data.length === 0) &&
|
(!data || data.length === 0) &&
|
||||||
(search !== "" || !isSearching) &&
|
(search !== "" || !isSearching) &&
|
||||||
searchPlaceholderKey && (
|
searchPlaceholderKey &&
|
||||||
|
!emptyState && (
|
||||||
<ListEmptyState
|
<ListEmptyState
|
||||||
hasIcon={true}
|
hasIcon={true}
|
||||||
icon={icon}
|
icon={icon}
|
||||||
|
|
|
@ -5,13 +5,16 @@ import {
|
||||||
ActionGroup,
|
ActionGroup,
|
||||||
AlertVariant,
|
AlertVariant,
|
||||||
Button,
|
Button,
|
||||||
|
Divider,
|
||||||
FormGroup,
|
FormGroup,
|
||||||
PageSection,
|
PageSection,
|
||||||
Select,
|
Select,
|
||||||
|
SelectGroup,
|
||||||
SelectOption,
|
SelectOption,
|
||||||
SelectVariant,
|
SelectVariant,
|
||||||
Switch,
|
Switch,
|
||||||
TextContent,
|
TextContent,
|
||||||
|
ToolbarItem,
|
||||||
} from "@patternfly/react-core";
|
} from "@patternfly/react-core";
|
||||||
|
|
||||||
import type RealmRepresentation from "keycloak-admin/lib/defs/realmRepresentation";
|
import type RealmRepresentation from "keycloak-admin/lib/defs/realmRepresentation";
|
||||||
|
@ -42,19 +45,21 @@ export const LocalizationTab = ({
|
||||||
save,
|
save,
|
||||||
reset,
|
reset,
|
||||||
realm,
|
realm,
|
||||||
refresh,
|
|
||||||
}: LocalizationTabProps) => {
|
}: LocalizationTabProps) => {
|
||||||
const { t } = useTranslation("realm-settings");
|
const { t } = useTranslation("realm-settings");
|
||||||
const adminClient = useAdminClient();
|
const adminClient = useAdminClient();
|
||||||
const [addMessageBundleModalOpen, setAddMessageBundleModalOpen] =
|
const [addMessageBundleModalOpen, setAddMessageBundleModalOpen] =
|
||||||
useState(false);
|
useState(false);
|
||||||
const [key, setKey] = useState(0);
|
|
||||||
|
|
||||||
const [supportedLocalesOpen, setSupportedLocalesOpen] = useState(false);
|
const [supportedLocalesOpen, setSupportedLocalesOpen] = useState(false);
|
||||||
const [defaultLocaleOpen, setDefaultLocaleOpen] = useState(false);
|
const [defaultLocaleOpen, setDefaultLocaleOpen] = useState(false);
|
||||||
|
const [filterDropdownOpen, setFilterDropdownOpen] = useState(false);
|
||||||
|
const [selectMenuLocale, setSelectMenuLocale] = useState("en");
|
||||||
|
|
||||||
const { getValues, control, handleSubmit } = useFormContext();
|
const { getValues, control, handleSubmit, formState } = useFormContext();
|
||||||
const [valueSelected, setValueSelected] = useState(false);
|
const [valueSelected, setValueSelected] = useState(false);
|
||||||
|
const [selectMenuValueSelected, setSelectMenuValueSelected] = useState(false);
|
||||||
|
|
||||||
const themeTypes = useServerInfo().themes!;
|
const themeTypes = useServerInfo().themes!;
|
||||||
const bundleForm = useForm<BundleForm>({ mode: "onChange" });
|
const bundleForm = useForm<BundleForm>({ mode: "onChange" });
|
||||||
const { addAlert, addError } = useAlerts();
|
const { addAlert, addError } = useAlerts();
|
||||||
|
@ -76,11 +81,25 @@ export const LocalizationTab = ({
|
||||||
try {
|
try {
|
||||||
const result = await adminClient.realms.getRealmLocalizationTexts({
|
const result = await adminClient.realms.getRealmLocalizationTexts({
|
||||||
realm: realm.realm!,
|
realm: realm.realm!,
|
||||||
selectedLocale: getValues("defaultLocale") || "en",
|
selectedLocale: selectMenuLocale || getValues("defaultLocale") || "en",
|
||||||
});
|
});
|
||||||
return Object.keys(result).map((key) => [key, result[key]]);
|
|
||||||
|
return Object.entries(result);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return [[]];
|
return [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const tableLoader = async () => {
|
||||||
|
try {
|
||||||
|
const result = await adminClient.realms.getRealmLocalizationTexts({
|
||||||
|
realm: currentRealm,
|
||||||
|
selectedLocale: selectMenuLocale,
|
||||||
|
});
|
||||||
|
|
||||||
|
return Object.entries(result);
|
||||||
|
} catch (error) {
|
||||||
|
return [];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -88,6 +107,34 @@ export const LocalizationTab = ({
|
||||||
setAddMessageBundleModalOpen(!addMessageBundleModalOpen);
|
setAddMessageBundleModalOpen(!addMessageBundleModalOpen);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const options = [
|
||||||
|
<SelectGroup label={t("defaultLocale")} key="group1">
|
||||||
|
{watchSupportedLocales
|
||||||
|
.filter((item) => item === realm?.defaultLocale)
|
||||||
|
.map((locale) => (
|
||||||
|
<SelectOption key={locale} value={locale}>
|
||||||
|
{t(`allSupportedLocales.${locale}`)}
|
||||||
|
</SelectOption>
|
||||||
|
))}
|
||||||
|
</SelectGroup>,
|
||||||
|
<Divider key="divider" />,
|
||||||
|
<SelectGroup label={t("supportedLocales")} key="group2">
|
||||||
|
{watchSupportedLocales
|
||||||
|
.filter((item) => item !== realm?.defaultLocale)
|
||||||
|
.map((locale) => (
|
||||||
|
<SelectOption key={locale} value={locale}>
|
||||||
|
{t(`allSupportedLocales.${locale}`)}
|
||||||
|
</SelectOption>
|
||||||
|
))}
|
||||||
|
</SelectGroup>,
|
||||||
|
];
|
||||||
|
|
||||||
|
const [tableKey, setTableKey] = useState(0);
|
||||||
|
|
||||||
|
const refreshTable = () => {
|
||||||
|
setTableKey(new Date().getTime());
|
||||||
|
};
|
||||||
|
|
||||||
const addKeyValue = async (pair: KeyValueType): Promise<void> => {
|
const addKeyValue = async (pair: KeyValueType): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
adminClient.setConfig({
|
adminClient.setConfig({
|
||||||
|
@ -96,7 +143,8 @@ export const LocalizationTab = ({
|
||||||
await adminClient.realms.addLocalization(
|
await adminClient.realms.addLocalization(
|
||||||
{
|
{
|
||||||
realm: currentRealm!,
|
realm: currentRealm!,
|
||||||
selectedLocale: getValues("defaultLocale") || "en",
|
selectedLocale:
|
||||||
|
selectMenuLocale || getValues("defaultLocale") || "en",
|
||||||
key: pair.key,
|
key: pair.key,
|
||||||
},
|
},
|
||||||
pair.value
|
pair.value
|
||||||
|
@ -105,7 +153,7 @@ export const LocalizationTab = ({
|
||||||
adminClient.setConfig({
|
adminClient.setConfig({
|
||||||
realmName: currentRealm!,
|
realmName: currentRealm!,
|
||||||
});
|
});
|
||||||
refresh();
|
refreshTable();
|
||||||
addAlert(t("pairCreatedSuccess"), AlertVariant.success);
|
addAlert(t("pairCreatedSuccess"), AlertVariant.success);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
addError("realm-settings:pairCreatedError", error);
|
addError("realm-settings:pairCreatedError", error);
|
||||||
|
@ -230,7 +278,6 @@ export const LocalizationTab = ({
|
||||||
onSelect={(_, value) => {
|
onSelect={(_, value) => {
|
||||||
onChange(value as string);
|
onChange(value as string);
|
||||||
setValueSelected(true);
|
setValueSelected(true);
|
||||||
setKey(new Date().getTime());
|
|
||||||
setDefaultLocaleOpen(false);
|
setDefaultLocaleOpen(false);
|
||||||
}}
|
}}
|
||||||
selections={
|
selections={
|
||||||
|
@ -269,6 +316,7 @@ export const LocalizationTab = ({
|
||||||
<ActionGroup>
|
<ActionGroup>
|
||||||
<Button
|
<Button
|
||||||
variant="primary"
|
variant="primary"
|
||||||
|
isDisabled={!formState.isDirty}
|
||||||
type="submit"
|
type="submit"
|
||||||
data-testid="localization-tab-save"
|
data-testid="localization-tab-save"
|
||||||
>
|
>
|
||||||
|
@ -287,12 +335,42 @@ export const LocalizationTab = ({
|
||||||
</TextContent>
|
</TextContent>
|
||||||
<div className="tableBorder">
|
<div className="tableBorder">
|
||||||
<KeycloakDataTable
|
<KeycloakDataTable
|
||||||
key={key}
|
key={tableKey}
|
||||||
loader={loader}
|
isSearching
|
||||||
ariaLabelKey="client-scopes:clientScopeList"
|
loader={selectMenuValueSelected ? tableLoader : loader}
|
||||||
|
ariaLabelKey="realm-settings:localization"
|
||||||
|
searchTypeComponent={
|
||||||
|
<ToolbarItem>
|
||||||
|
<Select
|
||||||
|
width={180}
|
||||||
|
data-testid="filter-by-locale-select"
|
||||||
|
isOpen={filterDropdownOpen}
|
||||||
|
className="kc-filter-by-locale-select"
|
||||||
|
variant={SelectVariant.single}
|
||||||
|
isDisabled={!formState.isSubmitSuccessful}
|
||||||
|
onToggle={(isExpanded) => setFilterDropdownOpen(isExpanded)}
|
||||||
|
onSelect={(_, value) => {
|
||||||
|
setSelectMenuLocale(value.toString());
|
||||||
|
setSelectMenuValueSelected(true);
|
||||||
|
refreshTable();
|
||||||
|
setFilterDropdownOpen(false);
|
||||||
|
}}
|
||||||
|
selections={
|
||||||
|
selectMenuValueSelected
|
||||||
|
? t(`allSupportedLocales.${selectMenuLocale}`)
|
||||||
|
: realm.defaultLocale !== ""
|
||||||
|
? t(`allSupportedLocales.${"en"}`)
|
||||||
|
: t("placeholderText")
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{options}
|
||||||
|
</Select>
|
||||||
|
</ToolbarItem>
|
||||||
|
}
|
||||||
toolbarItem={
|
toolbarItem={
|
||||||
<Button
|
<Button
|
||||||
data-testid="add-bundle-button"
|
data-testid="add-bundle-button"
|
||||||
|
isDisabled={!formState.isSubmitSuccessful}
|
||||||
onClick={() => setAddMessageBundleModalOpen(true)}
|
onClick={() => setAddMessageBundleModalOpen(true)}
|
||||||
>
|
>
|
||||||
{t("addMessageBundle")}
|
{t("addMessageBundle")}
|
||||||
|
|
|
@ -310,7 +310,6 @@ export const RealmSettingsSection = () => {
|
||||||
>
|
>
|
||||||
<EventsTab />
|
<EventsTab />
|
||||||
</Tab>
|
</Tab>
|
||||||
|
|
||||||
<Tab
|
<Tab
|
||||||
id="localization"
|
id="localization"
|
||||||
eventKey="localization"
|
eventKey="localization"
|
||||||
|
|
Loading…
Reference in a new issue