diff --git a/public/app/core/components/NestedFolderPicker/NestedFolderPicker.tsx b/public/app/core/components/NestedFolderPicker/NestedFolderPicker.tsx
index eaa737f69e6..a08ccf7f096 100644
--- a/public/app/core/components/NestedFolderPicker/NestedFolderPicker.tsx
+++ b/public/app/core/components/NestedFolderPicker/NestedFolderPicker.tsx
@@ -1,13 +1,11 @@
import { css } from '@emotion/css';
import React, { useCallback, useId, useMemo, useState } from 'react';
-import Skeleton from 'react-loading-skeleton';
import { usePopperTooltip } from 'react-popper-tooltip';
import { useAsync } from 'react-use';
import { GrafanaTheme2 } from '@grafana/data';
-import { Alert, Button, Icon, Input, LoadingBar, useStyles2 } from '@grafana/ui';
-import { Text } from '@grafana/ui/src/components/Text/Text';
-import { t, Trans } from 'app/core/internationalization';
+import { Alert, Icon, Input, LoadingBar, useStyles2 } from '@grafana/ui';
+import { t } from 'app/core/internationalization';
import { skipToken, useGetFolderQuery } from 'app/features/browse-dashboards/api/browseDashboardsAPI';
import { PAGE_SIZE } from 'app/features/browse-dashboards/api/services';
import {
@@ -26,6 +24,7 @@ import { DashboardViewItem } from 'app/features/search/types';
import { useDispatch, useSelector } from 'app/types/store';
import { getDOMId, NestedFolderList } from './NestedFolderList';
+import Trigger from './Trigger';
import { useTreeInteractions } from './hooks';
import { FolderChange, FolderUID } from './types';
@@ -198,22 +197,19 @@ export function NestedFolderPicker({ value, onChange }: NestedFolderPickerProps)
if (!visible) {
return (
-
+ aria-label={
+ label
+ ? t('browse-dashboards.folder-picker.accessible-label', 'Select folder: {{ label }} currently selected', {
+ label,
+ })
+ : undefined
+ }
+ />
);
}
@@ -222,6 +218,7 @@ export function NestedFolderPicker({ value, onChange }: NestedFolderPickerProps)
: null}
placeholder={label ?? t('browse-dashboards.folder-picker.search-placeholder', 'Search folders')}
value={search}
className={styles.search}
diff --git a/public/app/core/components/NestedFolderPicker/Trigger.tsx b/public/app/core/components/NestedFolderPicker/Trigger.tsx
new file mode 100644
index 00000000000..623fe81c49b
--- /dev/null
+++ b/public/app/core/components/NestedFolderPicker/Trigger.tsx
@@ -0,0 +1,96 @@
+import { css, cx } from '@emotion/css';
+import React, { forwardRef, ReactNode, ButtonHTMLAttributes } from 'react';
+import Skeleton from 'react-loading-skeleton';
+
+import { GrafanaTheme2 } from '@grafana/data';
+import { useStyles2, Icon, getInputStyles } from '@grafana/ui';
+import { focusCss } from '@grafana/ui/src/themes/mixins';
+import { Text } from '@grafana/ui/src/unstable';
+import { Trans } from 'app/core/internationalization';
+
+interface TriggerProps extends ButtonHTMLAttributes {
+ isLoading: boolean;
+ label?: ReactNode;
+}
+
+function Trigger({ isLoading, label, ...rest }: TriggerProps, ref: React.ForwardedRef) {
+ const styles = useStyles2(getStyles);
+
+ return (
+
+
+ {label ? (
+
+
+
+ ) : undefined}
+
+
+
+
+
+
+
+
+ );
+}
+
+export default forwardRef(Trigger);
+
+const getStyles = (theme: GrafanaTheme2) => {
+ const baseStyles = getInputStyles({ theme });
+
+ return {
+ wrapper: baseStyles.wrapper,
+ inputWrapper: baseStyles.inputWrapper,
+
+ prefix: css([
+ baseStyles.prefix,
+ {
+ pointerEvents: 'none',
+ color: theme.colors.text.primary,
+ },
+ ]),
+
+ suffix: css([
+ baseStyles.suffix,
+ {
+ pointerEvents: 'none',
+ },
+ ]),
+
+ fakeInput: css([
+ baseStyles.input,
+ {
+ textAlign: 'left',
+
+ letterSpacing: 'normal',
+
+ // We want the focus styles to appear only when tabbing through, not when clicking the button
+ // (and when focus is restored after command palette closes)
+ '&:focus': {
+ outline: 'unset',
+ boxShadow: 'unset',
+ },
+
+ '&:focus-visible': css`
+ ${focusCss(theme)}
+ `,
+ },
+ ]),
+
+ hasPrefix: css({
+ paddingLeft: 28,
+ }),
+ };
+};
diff --git a/public/locales/de-DE/grafana.json b/public/locales/de-DE/grafana.json
index 00a747069c7..701777cf7f8 100644
--- a/public/locales/de-DE/grafana.json
+++ b/public/locales/de-DE/grafana.json
@@ -62,6 +62,7 @@
"move": ""
},
"folder-picker": {
+ "accessible-label": "",
"button-label": "",
"empty-message": "",
"error-title": "",
diff --git a/public/locales/en-US/grafana.json b/public/locales/en-US/grafana.json
index c659816e5bb..cccb1961dcd 100644
--- a/public/locales/en-US/grafana.json
+++ b/public/locales/en-US/grafana.json
@@ -62,6 +62,7 @@
"move": "Move"
},
"folder-picker": {
+ "accessible-label": "Select folder: {{ label }} currently selected",
"button-label": "Select folder",
"empty-message": "No folders found",
"error-title": "Error loading folders",
diff --git a/public/locales/es-ES/grafana.json b/public/locales/es-ES/grafana.json
index fd6e4413b81..19fd1905aac 100644
--- a/public/locales/es-ES/grafana.json
+++ b/public/locales/es-ES/grafana.json
@@ -67,6 +67,7 @@
"move": ""
},
"folder-picker": {
+ "accessible-label": "",
"button-label": "",
"empty-message": "",
"error-title": "",
diff --git a/public/locales/fr-FR/grafana.json b/public/locales/fr-FR/grafana.json
index 953a7b92a56..1c7adcf59d6 100644
--- a/public/locales/fr-FR/grafana.json
+++ b/public/locales/fr-FR/grafana.json
@@ -67,6 +67,7 @@
"move": ""
},
"folder-picker": {
+ "accessible-label": "",
"button-label": "",
"empty-message": "",
"error-title": "",
diff --git a/public/locales/pseudo-LOCALE/grafana.json b/public/locales/pseudo-LOCALE/grafana.json
index b3f4ee574ed..85f021ff763 100644
--- a/public/locales/pseudo-LOCALE/grafana.json
+++ b/public/locales/pseudo-LOCALE/grafana.json
@@ -62,6 +62,7 @@
"move": "Mővę"
},
"folder-picker": {
+ "accessible-label": "Ŝęľęčŧ ƒőľđęř: {{ label }} čūřřęʼnŧľy şęľęčŧęđ",
"button-label": "Ŝęľęčŧ ƒőľđęř",
"empty-message": "Ńő ƒőľđęřş ƒőūʼnđ",
"error-title": "Ēřřőř ľőäđįʼnģ ƒőľđęřş",
diff --git a/public/locales/zh-Hans/grafana.json b/public/locales/zh-Hans/grafana.json
index 5af9c0ff9ac..24577491dab 100644
--- a/public/locales/zh-Hans/grafana.json
+++ b/public/locales/zh-Hans/grafana.json
@@ -57,6 +57,7 @@
"move": ""
},
"folder-picker": {
+ "accessible-label": "",
"button-label": "",
"empty-message": "",
"error-title": "",