diff --git a/pkg/lib/cockpit-components-file-autocomplete.jsx b/pkg/lib/cockpit-components-file-autocomplete.jsx
index f09002ba0539..7891df27dce3 100644
--- a/pkg/lib/cockpit-components-file-autocomplete.jsx
+++ b/pkg/lib/cockpit-components-file-autocomplete.jsx
@@ -19,9 +19,9 @@
import cockpit from "cockpit";
import React from "react";
-import { Select, SelectOption } from "@patternfly/react-core/dist/esm/deprecated/components/Select/index.js";
import PropTypes from "prop-types";
import { debounce } from 'throttle-debounce';
+import { TypeaheadSelect } from "cockpit-components-typeahead-select";
const _ = cockpit.gettext;
@@ -31,17 +31,11 @@ export class FileAutoComplete extends React.Component {
this.state = {
directory: '', // The current directory we list files/dirs from
displayFiles: [],
- isOpen: false,
value: this.props.value || null,
};
- this.typeaheadInputValue = "";
this.allowFilesUpdate = true;
- this.updateFiles = this.updateFiles.bind(this);
- this.finishUpdate = this.finishUpdate.bind(this);
- this.onToggle = this.onToggle.bind(this);
this.clearSelection = this.clearSelection.bind(this);
- this.onCreateOption = this.onCreateOption.bind(this);
this.onPathChange = (value) => {
if (!value) {
@@ -49,8 +43,6 @@ export class FileAutoComplete extends React.Component {
return;
}
- this.typeaheadInputValue = value;
-
const cb = (dirPath) => this.updateFiles(dirPath == '' ? '/' : dirPath);
let path = value;
@@ -89,12 +81,6 @@ export class FileAutoComplete extends React.Component {
this.allowFilesUpdate = false;
}
- onCreateOption(newValue) {
- this.setState(prevState => ({
- displayFiles: [...prevState.displayFiles, { type: "file", path: newValue }]
- }));
- }
-
updateFiles(path) {
if (this.state.directory == path)
return;
@@ -152,17 +138,9 @@ export class FileAutoComplete extends React.Component {
});
}
- onToggle(_, isOpen) {
- this.setState({ isOpen });
- }
-
clearSelection() {
- this.typeaheadInputValue = "";
this.updateFiles("/");
- this.setState({
- value: null,
- isOpen: false
- });
+ this.setState({ value: null });
this.props.onChange('', null);
}
@@ -170,32 +148,32 @@ export class FileAutoComplete extends React.Component {
const placeholder = this.props.placeholder || _("Path to file");
const selectOptions = this.state.displayFiles
- .map(option => );
+ .map(option => ({ value: option.path, content: option.path, className: option.type }));
+
return (
-
+ {
+ // Try to list again when
+ // opening. Calling onPathChange here
+ // usually does nothing, except when
+ // there was an error earlier.
+ if (isOpen)
+ this.onPathChange(this.state.value);
+ }}
+ selected={this.state.value}
+ onSelect={(_, value) => {
+ this.setState({ value });
+ this.onPathChange(value);
+ this.props.onChange(value || '', null);
+ }}
+ onClearSelection={this.clearSelection}
+ isCreatable={this.props.isOptionCreatable}
+ createOptionMessage={val => cockpit.format(_("Create $0"), val)}
+ selectOptions={selectOptions} />
);
}
}
diff --git a/test/common/testlib.py b/test/common/testlib.py
index f0d1161370ad..bd6a4edf6c6f 100644
--- a/test/common/testlib.py
+++ b/test/common/testlib.py
@@ -736,10 +736,12 @@ def set_input_text(
self.wait_val(selector, val)
def set_file_autocomplete_val(self, group_identifier: str, location: str) -> None:
- self.set_input_text(f"{group_identifier} .pf-v5-c-select__toggle-typeahead input", location)
- # click away the selection list, to force a state update
- self.click(f"{group_identifier} .pf-v5-c-select__toggle-typeahead")
- self.wait_not_present(f"{group_identifier} .pf-v5-c-select__menu")
+ self.set_input_text(f"{group_identifier} .pf-v5-c-menu-toggle input", location)
+ # select the file
+ self.wait_text(f"{group_identifier} ul li:nth-child(1) button", location)
+ self.click(f"{group_identifier} ul li:nth-child(1) button")
+ self.wait_not_present(f"{group_identifier} .pf-v5-c-menu")
+ self.wait_val(f"{group_identifier} .pf-v5-c-menu-toggle input", location)
@contextlib.contextmanager
def wait_timeout(self, timeout: int) -> Iterator[None]:
diff --git a/test/verify/check-pages b/test/verify/check-pages
index 25502cde2de5..ea321e3b3c8b 100755
--- a/test/verify/check-pages
+++ b/test/verify/check-pages
@@ -604,29 +604,28 @@ OnCalendar=daily
b.focus("#demo-file-ac input[type=text]")
b.input_text(stuff + "/")
# need to wait for the widget's "fast typing" inhibition delay to trigger the completion popup
- b.wait_in_text("#file-autocomplete-widget li:nth-of-type(1) button", stuff + "/")
- b.wait_in_text("#file-autocomplete-widget li:nth-of-type(2) button", "dir/")
- b.wait_in_text("#file-autocomplete-widget li:nth-of-type(3) button", "dir1/")
- b.wait_in_text("#file-autocomplete-widget li:nth-of-type(4) button", "file1.txt")
- b.click("#file-autocomplete-widget li:nth-of-type(2) button")
+ b.wait_in_text("#demo-file-ac li:nth-of-type(1) button", stuff + "/")
+ b.wait_in_text("#demo-file-ac li:nth-of-type(2) button", "dir/")
+ b.wait_in_text("#demo-file-ac li:nth-of-type(3) button", "dir1/")
+ b.wait_in_text("#demo-file-ac li:nth-of-type(4) button", "file1.txt")
+ b.click("#demo-file-ac li:nth-of-type(2) button")
# clear the file completion widget
b.click("#demo-file-ac div:first-of-type div:first-of-type button:nth-of-type(1)")
# test if input matches one entry, but is the prefix of other entry, widget should not descend into directory
b.focus("#demo-file-ac input[type=text]")
b.input_text(stuff + "/dir")
- b.wait_in_text("#file-autocomplete-widget li:nth-of-type(1) button", stuff + "/dir")
- b.wait_in_text("#file-autocomplete-widget li:nth-of-type(2) button", stuff + "/dir1")
+ b.wait_in_text("#demo-file-ac li:nth-of-type(1) button", stuff + "/dir")
+ b.wait_in_text("#demo-file-ac li:nth-of-type(2) button", stuff + "/dir1")
# clear the file completion widget
b.click("#demo-file-ac div:first-of-type div:first-of-type button:nth-of-type(1)")
- b.wait_not_present("#file-autocomplete-widget li")
b.focus("#demo-file-ac input[type=text]")
b.input_text(stuff + "/")
- b.wait_in_text("#file-autocomplete-widget li:nth-of-type(1) button", stuff + "/")
- b.wait_in_text("#file-autocomplete-widget li:nth-of-type(4) button", "file1.txt")
- b.click("#file-autocomplete-widget li:nth-of-type(4) button")
- b.wait_not_present("#file-autocomplete-widget li")
+ b.wait_in_text("#demo-file-ac li:nth-of-type(1) button", stuff + "/")
+ b.wait_in_text("#demo-file-ac li:nth-of-type(4) button", "file1.txt")
+ b.click("#demo-file-ac li:nth-of-type(4) button")
+ b.wait_not_present("#demo-file-ac li")
# now update file1, check robustness with dynamic events
m.execute(f"touch {stuff}/file1.txt")
@@ -634,10 +633,10 @@ OnCalendar=daily
time.sleep(1)
b.key("Backspace", 5)
# input is now $stuff/file
- b.wait_in_text("#file-autocomplete-widget li:nth-of-type(1) button", "file1.txt")
+ b.wait_in_text("#demo-file-ac li:nth-of-type(1) button", "file1.txt")
b.key("Backspace", 4)
# input is now $stuff/, so all listings should be available
- b.wait_in_text("#file-autocomplete-widget li:nth-of-type(4) button", "file1.txt")
+ b.wait_in_text("#demo-file-ac li:nth-of-type(4) button", "file1.txt")
# add new file
m.execute(f"touch {stuff}/other")
@@ -649,10 +648,10 @@ OnCalendar=daily
b.key("Backspace", 6)
time.sleep(1)
b.input_text("stuff/")
- b.wait_in_text("#file-autocomplete-widget li:nth-of-type(5) button", "other")
+ b.wait_in_text("#demo-file-ac li:nth-of-type(5) button", "other")
# close the selector
b.click("#demo-file-ac input")
- b.wait_not_present("#file-autocomplete-widget")
+ b.wait_not_present("#demo-file-ac li")
# Create test folder with known files
m.execute("mkdir /home/admin/newdir")
@@ -664,18 +663,15 @@ OnCalendar=daily
b.wait_val("#demo-file-ac-preselected input", "/home/admin/newdir/file1")
# open the selector
b.click("#demo-file-ac-preselected input")
- b.wait_visible("#file-autocomplete-widget-preselected")
+ b.wait_visible("#demo-file-ac-preselected .pf-v5-c-menu")
# close and open again to reload the dir (which just got created)
- b.click("#demo-file-ac-preselected input")
- b.wait_not_present("#file-autocomplete-widget-preselected")
+ b.click("#demo-file-ac-preselected .pf-v5-c-menu-toggle__button")
+ b.wait_not_present("#demo-file-ac-preselected .pf-v5-c-menu")
b.click("#demo-file-ac-preselected input")
# selection has all the files in the directory
paths = ["/home/admin/newdir", "/home/admin/newdir/dir1", "/home/admin/newdir/dir2", "/home/admin/newdir/file1", "/home/admin/newdir/file2"]
for i in range(5):
- b.wait_in_text(f"#file-autocomplete-widget-preselected li:nth-of-type({i + 1}) button", paths[i])
- # clicking on input again hides selector dropdown again
- b.click("#demo-file-ac-preselected input")
- b.wait_not_present("#file-autocomplete-widget-preselected")
+ b.wait_in_text(f"#demo-file-ac-preselected li:nth-of-type({i + 1}) button", paths[i])
@testlib.skipOstree("No PCP available")
@testlib.skipImage("pcp not currently in testing", "debian-testing")
@@ -956,7 +952,7 @@ OnCalendar=daily
# Upload permission error
b.drop_superuser()
- b.set_file_autocomplete_val("#demo-upload", "/root/")
+ b.set_file_autocomplete_val("#demo-upload", "/var/")
b.upload_files("#demo-upload input[type='file']", [test_upload_file])
b.wait_in_text(".pf-v5-c-alert", "Not permitted to perform this action")
diff --git a/test/verify/check-shell-keys b/test/verify/check-shell-keys
index 17c2030bef66..474f57ede02b 100755
--- a/test/verify/check-shell-keys
+++ b/test/verify/check-shell-keys
@@ -304,18 +304,17 @@ session optional pam_ssh_add.so
b.wait_visible("#credential-keys")
b.wait_not_present("#ssh-file-add")
b.click("#ssh-file-add-custom")
- b.set_file_autocomplete_val("#ssh-file-add-key", "/bad")
+ b.set_file_autocomplete_val("#ssh-file-add-key", "/etc/")
b.click("#ssh-file-add")
b.wait_text("#credentials-modal .pf-m-error > .pf-v5-c-helper-text__item-text", "Not a valid private key")
- b.click("#credentials-modal .pf-v5-c-select__toggle-typeahead")
- b.set_input_text("#credentials-modal .pf-v5-c-select__toggle-typeahead input", "/var/test/")
- b.wait_in_text("#credentials-modal .pf-v5-c-select__menu-item.pf-m-disabled", "No such file or directory")
- b.focus("#credentials-modal .pf-v5-c-select__toggle-typeahead input")
+ b.set_input_text("#credentials-modal .pf-v5-c-menu-toggle input", "/var/test/")
+ b.wait_in_text("#credentials-modal .pf-v5-c-menu__list-item.pf-m-aria-disabled", "No such file or directory")
+ b.focus("#credentials-modal .pf-v5-c-menu-toggle input")
b.key("Backspace", 5)
- b.wait_visible("#credentials-modal .pf-v5-c-select__menu-item.directory:contains('/var/lib/')")
- b.click("#credentials-modal .pf-v5-c-select__toggle-clear")
- b.wait_val("#credentials-modal .pf-v5-c-select__toggle-typeahead input", "")
+ b.wait_visible("#credentials-modal .pf-v5-c-menu__list-item.directory:contains('/var/lib/')")
+ b.click("#credentials-modal .pf-v5-c-menu-toggle button[aria-label='Clear input value']")
+ b.wait_val("#credentials-modal .pf-v5-c-menu-toggle input", "")
b.set_file_autocomplete_val("#ssh-file-add-key", "/tmp/new.rsa")
b.click("#ssh-file-add")