web: bad default in select (#8258)

* web: fix event propogation in search-select wrappers

Two different patches, an older one that extracted long search
blocks that were cut-and-pasted into a standalone component, and a
newer one that fixed displaying placeholder values properly,
conflicted and broke a relationship that allowed for the values to
be propagated through those standalone components correctly.

This restores the event handling and updates the listener set-ups
with more idiomatic hooks into Lit's event system.

* Updated search-select to properly render with Storybook, and provided a
foundation for testing the Search-Select component with Storybook.

* Accidentally deleted this line while making Sonar accept my test data.

* Fixing a small issue that's bugged me for awhile: there's no reason to manually duplicate what code can duplicate.

* Provided a storybook for testing out the flow search.

Discovered along the way that I'd mis-used a prop-drilling technique which caused the currentFlow
to be "undefined" when pass forward, giving rise to Marc's bug.

I *think* this shakes out the last of the bugs.  Events are passed up correctly and the initial value
is recorded correctly.

* Added comments and prettier had opinions.

* Restoring old variable names; they didn't have to change after all.

* fix lint

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
Ken Sternberg
2024-01-23 08:54:34 -08:00
committed by GitHub
parent 862aece9fc
commit 830689f1cb
7 changed files with 371 additions and 127 deletions

View File

@ -24,6 +24,7 @@
"semi": ["error", "always"], "semi": ["error", "always"],
"@typescript-eslint/ban-ts-comment": "off", "@typescript-eslint/ban-ts-comment": "off",
"sonarjs/cognitive-complexity": ["error", 9], "sonarjs/cognitive-complexity": ["error", 9],
"sonarjs/no-duplicate-string": "off",
"sonarjs/no-nested-template-literals": "off" "sonarjs/no-nested-template-literals": "off"
} }
} }

View File

@ -7,123 +7,72 @@
// Sometime around 2030 or so, the Javascript community may finally get its collective act together // Sometime around 2030 or so, the Javascript community may finally get its collective act together
// and we'll have one unified way of doing this. I can only hope. // and we'll have one unified way of doing this. I can only hope.
export const cssImportMaps = { const rawCssImportMaps = [
'import AKGlobal from "@goauthentik/common/styles/authentik.css";': 'import AKGlobal from "@goauthentik/common/styles/authentik.css";',
'import AKGlobal from "@goauthentik/common/styles/authentik.css?inline";', 'import PFAlert from "@patternfly/patternfly/components/Alert/alert.css";',
'import PFAlert from "@patternfly/patternfly/components/Alert/alert.css";': 'import PFAlertGroup from "@patternfly/patternfly/components/AlertGroup/alert-group.css";',
'import PFAlert from "@patternfly/patternfly/components/Alert/alert.css?inline";', 'import PFAvatar from "@patternfly/patternfly/components/Avatar/avatar.css";',
'import PFAlertGroup from "@patternfly/patternfly/components/AlertGroup/alert-group.css";': 'import PFBackdrop from "@patternfly/patternfly/components/Backdrop/backdrop.css";',
'import PFAlertGroup from "@patternfly/patternfly/components/AlertGroup/alert-group.css?inline";', 'import PFBackgroundImage from "@patternfly/patternfly/components/BackgroundImage/background-image.css";',
'import PFAvatar from "@patternfly/patternfly/components/Avatar/avatar.css";': 'import PFBanner from "@patternfly/patternfly/components/Banner/banner.css";',
'import PFAvatar from "@patternfly/patternfly/components/Avatar/avatar.css?inline";', 'import PFBase from "@patternfly/patternfly/patternfly-base.css";',
'import PFBackdrop from "@patternfly/patternfly/components/Backdrop/backdrop.css";': 'import PFBrand from "@patternfly/patternfly/components/Brand/brand.css";',
'import PFBackdrop from "@patternfly/patternfly/components/Backdrop/backdrop.css?inline";', 'import PFBullseye from "@patternfly/patternfly/layouts/Bullseye/bullseye.css";',
'import PFBackgroundImage from "@patternfly/patternfly/components/BackgroundImage/background-image.css";': 'import PFButton from "@patternfly/patternfly/components/Button/button.css";',
'import PFBackgroundImage from "@patternfly/patternfly/components/BackgroundImage/background-image.css?inline";', 'import PFCard from "@patternfly/patternfly/components/Card/card.css";',
'import PFBanner from "@patternfly/patternfly/components/Banner/banner.css";': 'import PFCheck from "@patternfly/patternfly/components/Check/check.css";',
'import PFBanner from "@patternfly/patternfly/components/Banner/banner.css?inline";', 'import PFChip from "@patternfly/patternfly/components/Chip/chip.css";',
'import PFBase from "@patternfly/patternfly/patternfly-base.css";': 'import PFChipGroup from "@patternfly/patternfly/components/ChipGroup/chip-group.css";',
'import PFBase from "@patternfly/patternfly/patternfly-base.css?inline";', 'import PFContent from "@patternfly/patternfly/components/Content/content.css";',
'import PFBrand from "@patternfly/patternfly/components/Brand/brand.css";': 'import PFDataList from "@patternfly/patternfly/components/DataList/data-list.css";',
'import PFBrand from "@patternfly/patternfly/components/Brand/brand.css?inline";', 'import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList/description-list.css";',
'import PFBullseye from "@patternfly/patternfly/layouts/Bullseye/bullseye.css";': 'import PFDisplay from "@patternfly/patternfly/utilities/Display/display.css";',
'import PFBullseye from "@patternfly/patternfly/layouts/Bullseye/bullseye.css?inline";', 'import PFDrawer from "@patternfly/patternfly/components/Drawer/drawer.css";',
'import PFButton from "@patternfly/patternfly/components/Button/button.css";': 'import PFDropdown from "@patternfly/patternfly/components/Dropdown/dropdown.css";',
'import PFButton from "@patternfly/patternfly/components/Button/button.css?inline";', 'import PFEmptyState from "@patternfly/patternfly/components/EmptyState/empty-state.css";',
'import PFCard from "@patternfly/patternfly/components/Card/card.css";': 'import PFExpandableSection from "@patternfly/patternfly/components/ExpandableSection/expandable-section.css";',
'import PFCard from "@patternfly/patternfly/components/Card/card.css?inline";', 'import PFFAIcons from "@patternfly/patternfly/base/patternfly-fa-icons.css";',
'import PFCheck from "@patternfly/patternfly/components/Check/check.css";': 'import PFFlex from "@patternfly/patternfly/layouts/Flex/flex.css";',
'import PFCheck from "@patternfly/patternfly/components/Check/check.css?inline";', 'import PFForm from "@patternfly/patternfly/components/Form/form.css";',
'import PFChip from "@patternfly/patternfly/components/Chip/chip.css";': 'import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";',
'import PFChip from "@patternfly/patternfly/components/Chip/chip.css?inline";', 'import PFGallery from "@patternfly/patternfly/layouts/Gallery/gallery.css";',
'import PFChipGroup from "@patternfly/patternfly/components/ChipGroup/chip-group.css";': 'import PFGlobal from "@patternfly/patternfly/patternfly-base.css";',
'import PFChipGroup from "@patternfly/patternfly/components/ChipGroup/chip-group.css?inline";', 'import PFGrid from "@patternfly/patternfly/layouts/Grid/grid.css";',
'import PFContent from "@patternfly/patternfly/components/Content/content.css";': 'import PFHint from "@patternfly/patternfly/components/Hint/hint.css";',
'import PFContent from "@patternfly/patternfly/components/Content/content.css?inline";', 'import PFInputGroup from "@patternfly/patternfly/components/InputGroup/input-group.css";',
'import PFDataList from "@patternfly/patternfly/components/DataList/data-list.css";': 'import PFLabel from "@patternfly/patternfly/components/Label/label.css";',
'import PFDataList from "@patternfly/patternfly/components/DataList/data-list.css?inline";', 'import PFList from "@patternfly/patternfly/components/List/list.css";',
'import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList/description-list.css";': 'import PFLogin from "@patternfly/patternfly/components/Login/login.css";',
'import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList/description-list.css?inline";', 'import PFModalBox from "@patternfly/patternfly/components/ModalBox/modal-box.css";',
'import PFDisplay from "@patternfly/patternfly/utilities/Display/display.css";': 'import PFNav from "@patternfly/patternfly/components/Nav/nav.css";',
'import PFDisplay from "@patternfly/patternfly/utilities/Display/display.css?inline";', 'import PFNotificationBadge from "@patternfly/patternfly/components/NotificationBadge/notification-badge.css";',
'import PFDrawer from "@patternfly/patternfly/components/Drawer/drawer.css";': 'import PFNotificationDrawer from "@patternfly/patternfly/components/NotificationDrawer/notification-drawer.css";',
'import PFDrawer from "@patternfly/patternfly/components/Drawer/drawer.css?inline";', 'import PFPage from "@patternfly/patternfly/components/Page/page.css";',
'import PFDropdown from "@patternfly/patternfly/components/Dropdown/dropdown.css";': 'import PFPagination from "@patternfly/patternfly/components/Pagination/pagination.css";',
'import PFDropdown from "@patternfly/patternfly/components/Dropdown/dropdown.css?inline";', 'import PFProgressStepper from "@patternfly/patternfly/components/ProgressStepper/progress-stepper.css";',
'import PFEmptyState from "@patternfly/patternfly/components/EmptyState/empty-state.css";': 'import PFRadio from "@patternfly/patternfly/components/Radio/radio.css";',
'import PFEmptyState from "@patternfly/patternfly/components/EmptyState/empty-state.css?inline";', 'import PFSelect from "@patternfly/patternfly/components/Select/select.css";',
'import PFExpandableSection from "@patternfly/patternfly/components/ExpandableSection/expandable-section.css";': 'import PFSidebar from "@patternfly/patternfly/components/Sidebar/sidebar.css";',
'import PFExpandableSection from "@patternfly/patternfly/components/ExpandableSection/expandable-section.css?inline";', 'import PFSizing from "@patternfly/patternfly/utilities/Sizing/sizing.css";',
'import PFFAIcons from "@patternfly/patternfly/base/patternfly-fa-icons.css";': 'import PFSpacing from "@patternfly/patternfly/utilities/Spacing/spacing.css";',
'import PFFAIcons from "@patternfly/patternfly/base/patternfly-fa-icons.css?inline";', 'import PFSpinner from "@patternfly/patternfly/components/Spinner/spinner.css";',
'import PFFlex from "@patternfly/patternfly/layouts/Flex/flex.css";': 'import PFStack from "@patternfly/patternfly/layouts/Stack/stack.css";',
'import PFFlex from "@patternfly/patternfly/layouts/Flex/flex.css?inline";', 'import PFSwitch from "@patternfly/patternfly/components/Switch/switch.css";',
'import PFForm from "@patternfly/patternfly/components/Form/form.css";': 'import PFTable from "@patternfly/patternfly/components/Table/table.css";',
'import PFForm from "@patternfly/patternfly/components/Form/form.css?inline";', 'import PFTabs from "@patternfly/patternfly/components/Tabs/tabs.css";',
'import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";': 'import PFTitle from "@patternfly/patternfly/components/Title/title.css";',
'import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css?inline";', 'import PFToggleGroup from "@patternfly/patternfly/components/ToggleGroup/toggle-group.css";',
'import PFGallery from "@patternfly/patternfly/layouts/Gallery/gallery.css";': 'import PFToolbar from "@patternfly/patternfly/components/Toolbar/toolbar.css";',
'import PFGallery from "@patternfly/patternfly/layouts/Gallery/gallery.css?inline";', 'import PFTreeView from "@patternfly/patternfly/components/TreeView/tree-view.css";',
'import PFGlobal from "@patternfly/patternfly/patternfly-base.css";': 'import PFWizard from "@patternfly/patternfly/components/Wizard/wizard.css";',
'import PFGlobal from "@patternfly/patternfly/patternfly-base.css?inline";', 'import ThemeDark from "@goauthentik/common/styles/theme-dark.css";',
'import PFGrid from "@patternfly/patternfly/layouts/Grid/grid.css";': 'import styles from "./LibraryPageImpl.css";',
'import PFGrid from "@patternfly/patternfly/layouts/Grid/grid.css?inline";', ];
'import PFHint from "@patternfly/patternfly/components/Hint/hint.css";':
'import PFHint from "@patternfly/patternfly/components/Hint/hint.css?inline";', const cssImportMaps = rawCssImportMaps.reduce(
'import PFInputGroup from "@patternfly/patternfly/components/InputGroup/input-group.css";': (acc, line) => ({ ...acc, [line]: line.replace(/\.css/, ".css?inline") }),
'import PFInputGroup from "@patternfly/patternfly/components/InputGroup/input-group.css?inline";', {},
'import PFLabel from "@patternfly/patternfly/components/Label/label.css";': );
'import PFLabel from "@patternfly/patternfly/components/Label/label.css?inline";',
'import PFList from "@patternfly/patternfly/components/List/list.css";': export { cssImportMaps };
'import PFList from "@patternfly/patternfly/components/List/list.css?inline";', export default cssImportMaps;
'import PFLogin from "@patternfly/patternfly/components/Login/login.css";':
'import PFLogin from "@patternfly/patternfly/components/Login/login.css?inline";',
'import PFModalBox from "@patternfly/patternfly/components/ModalBox/modal-box.css";':
'import PFModalBox from "@patternfly/patternfly/components/ModalBox/modal-box.css?inline";',
'import PFNav from "@patternfly/patternfly/components/Nav/nav.css";':
'import PFNav from "@patternfly/patternfly/components/Nav/nav.css?inline";',
'import PFNotificationBadge from "@patternfly/patternfly/components/NotificationBadge/notification-badge.css";':
'import PFNotificationBadge from "@patternfly/patternfly/components/NotificationBadge/notification-badge.css?inline";',
'import PFNotificationDrawer from "@patternfly/patternfly/components/NotificationDrawer/notification-drawer.css";':
'import PFNotificationDrawer from "@patternfly/patternfly/components/NotificationDrawer/notification-drawer.css?inline";',
'import PFPage from "@patternfly/patternfly/components/Page/page.css";':
'import PFPage from "@patternfly/patternfly/components/Page/page.css?inline";',
'import PFPagination from "@patternfly/patternfly/components/Pagination/pagination.css";':
'import PFPagination from "@patternfly/patternfly/components/Pagination/pagination.css?inline";',
'import PFProgressStepper from "@patternfly/patternfly/components/ProgressStepper/progress-stepper.css";':
'import PFProgressStepper from "@patternfly/patternfly/components/ProgressStepper/progress-stepper.css?inline";',
'import PFRadio from "@patternfly/patternfly/components/Radio/radio.css";':
'import PFRadio from "@patternfly/patternfly/components/Radio/radio.css?inline";',
'import PFSelect from "@patternfly/patternfly/components/Select/select.css";':
'import PFSelect from "@patternfly/patternfly/components/Select/select.css?inline";',
'import PFSidebar from "@patternfly/patternfly/components/Sidebar/sidebar.css";':
'import PFSidebar from "@patternfly/patternfly/components/Sidebar/sidebar.css?inline";',
'import PFSizing from "@patternfly/patternfly/utilities/Sizing/sizing.css";':
'import PFSizing from "@patternfly/patternfly/utilities/Sizing/sizing.css?inline";',
'import PFSpacing from "@patternfly/patternfly/utilities/Spacing/spacing.css";':
'import PFSpacing from "@patternfly/patternfly/utilities/Spacing/spacing.css?inline";',
'import PFSpinner from "@patternfly/patternfly/components/Spinner/spinner.css";':
'import PFSpinner from "@patternfly/patternfly/components/Spinner/spinner.css?inline";',
'import PFStack from "@patternfly/patternfly/layouts/Stack/stack.css";':
'import PFStack from "@patternfly/patternfly/layouts/Stack/stack.css?inline";',
'import PFSwitch from "@patternfly/patternfly/components/Switch/switch.css";':
'import PFSwitch from "@patternfly/patternfly/components/Switch/switch.css?inline";',
'import PFTable from "@patternfly/patternfly/components/Table/table.css";':
'import PFTable from "@patternfly/patternfly/components/Table/table.css?inline";',
'import PFTabs from "@patternfly/patternfly/components/Tabs/tabs.css";':
'import PFTabs from "@patternfly/patternfly/components/Tabs/tabs.css?inline";',
'import PFTitle from "@patternfly/patternfly/components/Title/title.css";':
'import PFTitle from "@patternfly/patternfly/components/Title/title.css?inline";',
'import PFToggleGroup from "@patternfly/patternfly/components/ToggleGroup/toggle-group.css";':
'import PFToggleGroup from "@patternfly/patternfly/components/ToggleGroup/toggle-group.css?inline";',
'import PFToolbar from "@patternfly/patternfly/components/Toolbar/toolbar.css";':
'import PFToolbar from "@patternfly/patternfly/components/Toolbar/toolbar.css?inline";',
'import PFTreeView from "@patternfly/patternfly/components/TreeView/tree-view.css";':
'import PFTreeView from "@patternfly/patternfly/components/TreeView/tree-view.css?inline";',
'import PFWizard from "@patternfly/patternfly/components/Wizard/wizard.css";':
'import PFWizard from "@patternfly/patternfly/components/Wizard/wizard.css?inline";',
'import ThemeDark from "@goauthentik/common/styles/theme-dark.css";':
'import ThemeDark from "@goauthentik/common/styles/theme-dark.css?inline";',
'import styles from "./LibraryPageImpl.css";':
'import styles from "./LibraryPageImpl.css?inline";',
};

View File

@ -39,7 +39,7 @@ function createOneImportLine(line: string) {
if (!importContent) { if (!importContent) {
throw new Error("How did an unmatchable line get here!?"); throw new Error("How did an unmatchable line get here!?");
} }
return `'${importContent}";': '${importContent}?inline";',`; return ` '${importContent}";',`;
} }
const isSourceFile = /\.ts$/; const isSourceFile = /\.ts$/;
@ -73,9 +73,15 @@ const outputFile = `
// Sometime around 2030 or so, the Javascript community may finally get its collective act together // Sometime around 2030 or so, the Javascript community may finally get its collective act together
// and we'll have one unified way of doing this. I can only hope. // and we'll have one unified way of doing this. I can only hope.
export const cssImportMaps = { const rawCssImportMaps = [
${importLines.map(createOneImportLine).join("\n")} ${importLines.map(createOneImportLine).join("\n")}
}; ];
const cssImportMaps = rawCssImportMaps.reduce((acc, line) => (
{...acc, [line]: line.replace(/\\.css/, ".css?inline")}), {});
export { cssImportMaps };
export default cssImportMaps;
`; `;
fs.writeFileSync(path.join(__dirname, "..", ".storybook", "css-import-maps.ts"), outputFile, { fs.writeFileSync(path.join(__dirname, "..", ".storybook", "css-import-maps.ts"), outputFile, {

View File

@ -46,8 +46,8 @@ export class FlowSearch<T extends Flow> extends CustomListenerElement(AKElement)
* *
* @attr * @attr
*/ */
@property({ attribute: false }) @property({ type: String })
currentFlow: string | undefined; currentFlow?: string | undefined;
/** /**
* If true, it is not valid to leave the flow blank. * If true, it is not valid to leave the flow blank.

View File

@ -0,0 +1,136 @@
import "@goauthentik/admin/common/ak-flow-search/ak-flow-search";
import { AkFlowSearch } from "@goauthentik/admin/common/ak-flow-search/ak-flow-search";
import "@goauthentik/elements/forms/HorizontalFormElement";
import { Meta } from "@storybook/web-components";
import { msg } from "@lit/localize";
import { TemplateResult, html } from "lit";
import { Flow, FlowsInstancesListDesignationEnum } from "@goauthentik/api";
const mockData = {
pagination: {
next: 0,
previous: 0,
count: 2,
current: 1,
total_pages: 1,
start_index: 1,
end_index: 2,
},
results: [
{
pk: "41468774-bef6-4ffb-b675-332d0d8c5d25",
policybindingmodel_ptr_id: "0fb5b872-2734-44bd-ac7e-f23051481a83",
name: "Authorize Application",
slug: "default-provider-authorization-explicit-consent",
title: "Redirecting to %(app)s",
designation: "authorization",
background: "/static/dist/assets/images/flow_background.jpg",
stages: ["8adcdc74-0d3d-48a8-b628-38e3da4081e5"],
policies: [],
cache_count: 0,
policy_engine_mode: "any",
compatibility_mode: false,
export_url:
"/api/v3/flows/instances/default-provider-authorization-explicit-consent/export/",
layout: "stacked",
denied_action: "message_continue",
authentication: "require_authenticated",
},
{
pk: "89f57fd8-fd1e-42be-a5fd-abc13b19529b",
policybindingmodel_ptr_id: "e8526408-c6ee-46e1-bbfe-a1d37c2c02c8",
name: "Authorize Application",
slug: "default-provider-authorization-implicit-consent",
title: "Redirecting to %(app)s",
designation: "authorization",
background: "/static/dist/assets/images/flow_background.jpg",
stages: [],
policies: [],
cache_count: 0,
policy_engine_mode: "any",
compatibility_mode: false,
export_url:
"/api/v3/flows/instances/default-provider-authorization-implicit-consent/export/",
layout: "stacked",
denied_action: "message_continue",
authentication: "require_authenticated",
},
],
};
const metadata: Meta<AkFlowSearch<Flow>> = {
title: "Elements / Select Search / Flow",
component: "ak-flow-search",
parameters: {
docs: {
description: {
component: "A Select Search for Authentication Flows",
},
},
mockData: [
{
url: `${window.location.origin}/api/v3/flows/instances/?designation=authorization&ordering=slug`,
method: "GET",
status: 200,
response: () => mockData,
},
],
},
};
export default metadata;
const container = (testItem: TemplateResult) => {
return html` <div style="background: #fff; padding: 1.0rem;">
<style>
li {
display: block;
}
p {
margin-top: 1em;
}
</style>
${testItem}
<ul id="message-pad" style="margin-top: 1em; min-height: 5em;"></ul>
</div>`;
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const displayChange = (ev: any) => {
document.getElementById("message-pad")!.innerText = `Value selected: ${JSON.stringify(
ev.target.value,
null,
2,
)}`;
};
export const Default = () =>
container(
html` <ak-form-element-horizontal
label=${msg("Authorization flow")}
?required=${true}
name="authorizationFlow"
>
<ak-flow-search
flowType=${FlowsInstancesListDesignationEnum.Authorization}
@input=${displayChange}
></ak-flow-search
></ak-form-element-horizontal>`,
);
export const WithInitialValue = () =>
container(
html` <ak-form-element-horizontal
label=${msg("Authorization flow")}
?required=${true}
name="authorizationFlow"
>
<ak-flow-search
flowType=${FlowsInstancesListDesignationEnum.Authorization}
currentFlow="89f57fd8-fd1e-42be-a5fd-abc13b19529b"
@input=${displayChange}
></ak-flow-search
></ak-form-element-horizontal>`,
);

View File

@ -0,0 +1,145 @@
import { groupBy } from "@goauthentik/app/common/utils";
import { convertToSlug as slugify } from "@goauthentik/common/utils.js";
import "@goauthentik/elements/forms/SearchSelect/ak-search-select";
import { SearchSelect } from "@goauthentik/elements/forms/SearchSelect/ak-search-select";
import { Meta } from "@storybook/web-components";
import { TemplateResult, html } from "lit";
type RawSample = [string, string[]];
type Sample = { name: string; pk: string; season: string[] };
// prettier-ignore
const groupedSamples: RawSample[] = [
["Spring", [
"Apples", "Apricots", "Asparagus", "Avocados", "Bananas", "Broccoli",
"Cabbage", "Carrots", "Celery", "Collard Greens", "Garlic", "Herbs", "Kale", "Kiwifruit", "Lemons",
"Lettuce", "Limes", "Mushrooms", "Onions", "Peas", "Pineapples", "Radishes", "Rhubarb", "Spinach",
"Strawberries", "Swiss Chard", "Turnips"]],
["Summer", [
"Apples", "Apricots", "Avocados", "Bananas", "Beets", "Bell Peppers", "Blackberries", "Blueberries",
"Cantaloupe", "Carrots", "Celery", "Cherries", "Corn", "Cucumbers", "Eggplant", "Garlic",
"Green Beans", "Herbs", "Honeydew Melon", "Lemons", "Lima Beans", "Limes", "Mangos", "Okra", "Peaches",
"Plums", "Raspberries", "Strawberries", "Summer Squash", "Tomatillos", "Tomatoes", "Watermelon",
"Zucchini"]],
["Fall", [
"Apples", "Bananas", "Beets", "Bell Peppers", "Broccoli", "Brussels Sprouts", "Cabbage", "Carrots",
"Cauliflower", "Celery", "Collard Greens", "Cranberries", "Garlic", "Ginger", "Grapes", "Green Beans",
"Herbs", "Kale", "Kiwifruit", "Lemons", "Lettuce", "Limes", "Mangos", "Mushrooms", "Onions",
"Parsnips", "Pears", "Peas", "Pineapples", "Potatoes", "Pumpkin", "Radishes", "Raspberries",
"Rutabagas", "Spinach", "Sweet Potatoes", "Swiss Chard", "Turnips", "Winter Squash"]],
["Winter", [
"Apples", "Avocados", "Bananas", "Beets", "Brussels Sprouts", "Cabbage", "Carrots", "Celery",
"Collard Greens", "Grapefruit", "Herbs", "Kale", "Kiwifruit", "Leeks", "Lemons", "Limes", "Onions",
"Oranges", "Parsnips", "Pears", "Pineapples", "Potatoes", "Pumpkin", "Rutabagas",
"Sweet Potatoes", "Swiss Chard", "Turnips", "Winter Squash"]]
];
// WAAAAY too many lines to turn the arrays above into a Sample of
// { name: "Apricots", pk: "apple", season: ["Spring", "Summer"] }
// but it does the job.
const samples = Array.from(
groupedSamples
.reduce((acc, sample) => {
sample[1].forEach((item) => {
const update = (thing: Sample) => ({
...thing,
season: [...thing.season, sample[0]],
});
acc.set(
item,
update(acc.get(item) || { name: item, pk: slugify(item), season: [] }),
);
return acc;
}, acc);
return acc;
}, new Map<string, Sample>())
.values(),
);
samples.sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
// All we need is a promise to return our dataset. It doesn't have to be a class-based method a'la
// the authentik API.
const getSamples = (query = "") =>
Promise.resolve(
samples.filter((s) =>
query !== "" ? s.name.toLowerCase().includes(query.toLowerCase()) : true,
),
);
const metadata: Meta<SearchSelect<Sample>> = {
title: "Elements / Search Select ",
component: "ak-search-select",
parameters: {
docs: {
description: {
component: "An implementation of the Patternfly search select pattern",
},
},
},
};
export default metadata;
const container = (testItem: TemplateResult) =>
html` <div style="background: #fff; padding: 2em">
<style>
li {
display: block;
}
p {
margin-top: 1em;
}
</style>
${testItem}
<ul id="message-pad" style="margin-top: 1em"></ul>
</div>`;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const displayChange = (ev: any) => {
document.getElementById("message-pad")!.innerText = `Value selected: ${JSON.stringify(
ev.detail.value,
null,
2,
)}`;
};
export const Default = () => {
return container(
html`<ak-search-select
.fetchObjects=${getSamples}
.renderElement=${(sample: Sample) => sample.name}
.value=${(sample: Sample) => sample.pk}
@ak-change=${displayChange}
></ak-search-select>`,
);
};
export const Grouped = () => {
return container(
html`<ak-search-select
.fetchObjects=${getSamples}
.renderElement=${(sample: Sample) => sample.name}
.value=${(sample: Sample) => sample.pk}
.groupBy=${(samples: Sample[]) =>
groupBy(samples, (sample: Sample) => sample.season[0] ?? "")}
@ak-change=${displayChange}
></ak-search-select>`,
);
};
export const Selected = () => {
return container(
html`<ak-search-select
.fetchObjects=${getSamples}
.renderElement=${(sample: Sample) => sample.name}
.value=${(sample: Sample) => sample.pk}
.selected=${(sample: Sample) => sample.pk === "herbs"}
@ak-change=${displayChange}
></ak-search-select>`,
);
};

View File

@ -3,6 +3,7 @@ import { PreventFormSubmit } from "@goauthentik/app/elements/forms/helpers";
import { EVENT_REFRESH } from "@goauthentik/common/constants"; import { EVENT_REFRESH } from "@goauthentik/common/constants";
import { ascii_letters, digits, groupBy, randomString } from "@goauthentik/common/utils"; import { ascii_letters, digits, groupBy, randomString } from "@goauthentik/common/utils";
import { AKElement } from "@goauthentik/elements/Base"; import { AKElement } from "@goauthentik/elements/Base";
import { ensureCSSStyleSheet } from "@goauthentik/elements/utils/ensureCSSStyleSheet";
import { CustomEmitterElement } from "@goauthentik/elements/utils/eventEmitter"; import { CustomEmitterElement } from "@goauthentik/elements/utils/eventEmitter";
import { msg, str } from "@lit/localize"; import { msg, str } from "@lit/localize";
@ -105,12 +106,17 @@ export class SearchSelect<T> extends CustomEmitterElement(AKElement) {
@state() @state()
error?: APIErrorTypes; error?: APIErrorTypes;
static styles = [PFBase, PFForm, PFFormControl, PFSelect]; static get styles() {
return [PFBase, PFForm, PFFormControl, PFSelect];
}
constructor() { constructor() {
super(); super();
if (!document.adoptedStyleSheets.includes(PFDropdown)) { if (!document.adoptedStyleSheets.includes(PFDropdown)) {
document.adoptedStyleSheets = [...document.adoptedStyleSheets, PFDropdown]; document.adoptedStyleSheets = [
...document.adoptedStyleSheets,
ensureCSSStyleSheet(PFDropdown),
];
} }
this.dropdownContainer = document.createElement("div"); this.dropdownContainer = document.createElement("div");
this.observer = new IntersectionObserver(() => { this.observer = new IntersectionObserver(() => {
@ -150,6 +156,7 @@ export class SearchSelect<T> extends CustomEmitterElement(AKElement) {
objects.forEach((obj) => { objects.forEach((obj) => {
if (this.selected && this.selected(obj, objects || [])) { if (this.selected && this.selected(obj, objects || [])) {
this.selectedObject = obj; this.selectedObject = obj;
this.dispatchCustomEvent("ak-change", { value: this.selectedObject });
} }
}); });
this.objects = objects; this.objects = objects;