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:
Jenny 2021-08-17 08:50:31 -04:00 committed by GitHub
parent c575ffa491
commit f93d478f44
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 97 additions and 21 deletions

View file

@ -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" />

View file

@ -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}

View file

@ -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")}

View file

@ -310,7 +310,6 @@ export const RealmSettingsSection = () => {
> >
<EventsTab /> <EventsTab />
</Tab> </Tab>
<Tab <Tab
id="localization" id="localization"
eventKey="localization" eventKey="localization"