mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Select: Fix input cursor position so that it is at the start for single value selects (#41693)
* Select: Fix input cursor position so that it is at the start for single value selects * Fixing e2e tests * Fixes cursor issue * Fixing e2e tests * e2e fix * Select: ensure input always overlays singleValue, update pa11y config Co-authored-by: Ashley Harrison <ashley.harrison@grafana.com>
This commit is contained in:
parent
130386f84b
commit
9f4aa472cf
@ -6,7 +6,9 @@ var config = {
|
|||||||
chromeLaunchConfig: {
|
chromeLaunchConfig: {
|
||||||
args: ['--no-sandbox'],
|
args: ['--no-sandbox'],
|
||||||
},
|
},
|
||||||
hideElements: '#updateVersion',
|
// see https://github.com/grafana/grafana/pull/41693#issuecomment-979921463 for context
|
||||||
|
// on why we're ignoring singleValue/react-select-*-placeholder elements
|
||||||
|
hideElements: '#updateVersion, [class*="-singleValue"], [id^="react-select-"][id$="-placeholder"]',
|
||||||
},
|
},
|
||||||
|
|
||||||
urls: [
|
urls: [
|
||||||
|
@ -6,7 +6,9 @@ var config = {
|
|||||||
chromeLaunchConfig: {
|
chromeLaunchConfig: {
|
||||||
args: ['--no-sandbox'],
|
args: ['--no-sandbox'],
|
||||||
},
|
},
|
||||||
hideElements: '#updateVersion',
|
// see https://github.com/grafana/grafana/pull/41693#issuecomment-979921463 for context
|
||||||
|
// on why we're ignoring singleValue/react-select-*-placeholder elements
|
||||||
|
hideElements: '#updateVersion, [class*="-singleValue"], [id^="react-select-"][id$="-placeholder"]',
|
||||||
},
|
},
|
||||||
|
|
||||||
urls: [
|
urls: [
|
||||||
|
@ -53,7 +53,7 @@ e2e.scenario({
|
|||||||
e2e.components.TimeZonePicker.containerV2()
|
e2e.components.TimeZonePicker.containerV2()
|
||||||
.should('be.visible')
|
.should('be.visible')
|
||||||
.within(() => {
|
.within(() => {
|
||||||
e2e.components.Select.singleValue().should('be.visible').should('have.text', 'Coordinated Universal Time');
|
e2e.components.Select.singleValue().should('have.text', 'Coordinated Universal Time');
|
||||||
e2e.components.Select.input().should('be.visible').click();
|
e2e.components.Select.input().should('be.visible').click();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ describe('Variables - Add variable', () => {
|
|||||||
e2e.pages.Dashboard.Settings.Variables.Edit.General.generalTypeSelect()
|
e2e.pages.Dashboard.Settings.Variables.Edit.General.generalTypeSelect()
|
||||||
.should('be.visible')
|
.should('be.visible')
|
||||||
.within((select) => {
|
.within((select) => {
|
||||||
e2e.components.Select.singleValue().should('be.visible').should('have.text', 'Query');
|
e2e.components.Select.singleValue().should('have.text', 'Query');
|
||||||
});
|
});
|
||||||
e2e.pages.Dashboard.Settings.Variables.Edit.General.generalLabelInput()
|
e2e.pages.Dashboard.Settings.Variables.Edit.General.generalLabelInput()
|
||||||
.should('be.visible')
|
.should('be.visible')
|
||||||
@ -82,7 +82,7 @@ describe('Variables - Add variable', () => {
|
|||||||
|
|
||||||
e2e().get('#Description').should('be.visible').clear().type('a description');
|
e2e().get('#Description').should('be.visible').clear().type('a description');
|
||||||
|
|
||||||
e2e.components.DataSourcePicker.inputV2().should('be.visible').type('gdev-testdata{enter}');
|
e2e.components.DataSourcePicker.container().should('be.visible').type('gdev-testdata{enter}');
|
||||||
|
|
||||||
e2e.pages.Dashboard.Settings.Variables.Edit.QueryVariable.queryOptionsQueryInput()
|
e2e.pages.Dashboard.Settings.Variables.Edit.QueryVariable.queryOptionsQueryInput()
|
||||||
.should('be.visible')
|
.should('be.visible')
|
||||||
@ -132,7 +132,7 @@ describe('Variables - Add variable', () => {
|
|||||||
|
|
||||||
e2e().get('#Description').should('be.visible').clear().type('a description');
|
e2e().get('#Description').should('be.visible').clear().type('a description');
|
||||||
|
|
||||||
e2e.components.DataSourcePicker.inputV2().should('be.visible').type('gdev-testdata{enter}');
|
e2e.components.DataSourcePicker.container().type('gdev-testdata{enter}');
|
||||||
|
|
||||||
e2e.pages.Dashboard.Settings.Variables.Edit.QueryVariable.queryOptionsQueryInput()
|
e2e.pages.Dashboard.Settings.Variables.Edit.QueryVariable.queryOptionsQueryInput()
|
||||||
.should('be.visible')
|
.should('be.visible')
|
||||||
|
@ -44,7 +44,7 @@ describe('Exemplars', () => {
|
|||||||
|
|
||||||
e2e.pages.Explore.visit();
|
e2e.pages.Explore.visit();
|
||||||
|
|
||||||
e2e.components.DataSourcePicker.input().should('be.visible').click();
|
e2e.components.DataSourcePicker.container().should('be.visible').click();
|
||||||
e2e().contains(dataSourceName).scrollIntoView().should('be.visible').click();
|
e2e().contains(dataSourceName).scrollIntoView().should('be.visible').click();
|
||||||
|
|
||||||
// we need to wait for the query-field being lazy-loaded, in two steps:
|
// we need to wait for the query-field being lazy-loaded, in two steps:
|
||||||
|
@ -8,7 +8,7 @@ e2e.scenario({
|
|||||||
skipScenario: false,
|
skipScenario: false,
|
||||||
scenario: () => {
|
scenario: () => {
|
||||||
e2e.pages.Explore.visit();
|
e2e.pages.Explore.visit();
|
||||||
e2e.components.DataSourcePicker.inputV2().should('be.visible').click();
|
e2e.components.DataSourcePicker.container().should('be.visible').click();
|
||||||
|
|
||||||
cy.contains('gdev-prometheus').scrollIntoView().should('be.visible').click();
|
cy.contains('gdev-prometheus').scrollIntoView().should('be.visible').click();
|
||||||
const queryText = 'http_requests_total';
|
const queryText = 'http_requests_total';
|
||||||
|
@ -11,9 +11,7 @@ describe('Trace view', () => {
|
|||||||
|
|
||||||
e2e.pages.Explore.visit();
|
e2e.pages.Explore.visit();
|
||||||
|
|
||||||
e2e.components.DataSourcePicker.inputV2().should('be.visible').click();
|
e2e.components.DataSourcePicker.container().should('be.visible').type('gdev-jaeger{enter}');
|
||||||
|
|
||||||
e2e().contains('gdev-jaeger').scrollIntoView().should('be.visible').click();
|
|
||||||
|
|
||||||
e2e.components.QueryField.container().should('be.visible').type('long-trace');
|
e2e.components.QueryField.container().should('be.visible').type('long-trace');
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ export const importDashboard = (dashboardToImport: Dashboard, queryTimeout?: num
|
|||||||
e2e.components.Panels.Panel.title(panel.title).should('be.visible').click();
|
e2e.components.Panels.Panel.title(panel.title).should('be.visible').click();
|
||||||
e2e.components.Panels.Panel.headerItems('Inspect').should('be.visible').click();
|
e2e.components.Panels.Panel.headerItems('Inspect').should('be.visible').click();
|
||||||
e2e.components.Tab.title('JSON').should('be.visible').click();
|
e2e.components.Tab.title('JSON').should('be.visible').click();
|
||||||
e2e.components.PanelInspector.Json.content().should('be.visible').contains('Panel JSON').click();
|
e2e.components.PanelInspector.Json.content().should('be.visible').contains('Panel JSON').click({ force: true });
|
||||||
e2e.components.Select.option().should('be.visible').contains('Data').click();
|
e2e.components.Select.option().should('be.visible').contains('Data').click();
|
||||||
|
|
||||||
// ensures that panel has loaded without knowingly hitting an error
|
// ensures that panel has loaded without knowingly hitting an error
|
||||||
|
@ -25,15 +25,12 @@ const getInputControlStyles = stylesFactory(
|
|||||||
css`
|
css`
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
|
||||||
padding-right: 0;
|
padding-right: 0;
|
||||||
|
|
||||||
position: relative;
|
position: relative;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
`,
|
`,
|
||||||
|
@ -260,7 +260,7 @@ export function SelectBase<T>({
|
|||||||
css`
|
css`
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
color: ${theme.colors.text.disabled};
|
color: ${theme.colors.text.disabled};
|
||||||
|
grid-area: 1 / 1 / 2 / 3;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
@ -18,8 +18,9 @@ const getStyles = (theme: GrafanaTheme2) => {
|
|||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
|
grid-area: 1 / 1 / 2 / 3;
|
||||||
`;
|
`;
|
||||||
const container = css`
|
const spinnerWrapper = css`
|
||||||
width: 16px;
|
width: 16px;
|
||||||
height: 16px;
|
height: 16px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
@ -29,7 +30,7 @@ const getStyles = (theme: GrafanaTheme2) => {
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const item = css`
|
const spinnerIcon = css`
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -39,7 +40,7 @@ const getStyles = (theme: GrafanaTheme2) => {
|
|||||||
color: ${tinycolor(theme.colors.text.disabled).setAlpha(0.64).toString()};
|
color: ${tinycolor(theme.colors.text.disabled).setAlpha(0.64).toString()};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
return { singleValue, container, item, disabled };
|
return { singleValue, spinnerWrapper, spinnerIcon, disabled };
|
||||||
};
|
};
|
||||||
|
|
||||||
type StylesType = ReturnType<typeof getStyles>;
|
type StylesType = ReturnType<typeof getStyles>;
|
||||||
@ -52,36 +53,34 @@ export const SingleValue = <T extends unknown>(props: Props<T>) => {
|
|||||||
const loading = useDelayedSwitch(data.loading || false, { delay: 250, duration: 750 });
|
const loading = useDelayedSwitch(data.loading || false, { delay: 250, duration: 750 });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<components.SingleValue {...props}>
|
<components.SingleValue {...props} className={cx(styles.singleValue, isDisabled && styles.disabled)}>
|
||||||
<div className={cx(styles.singleValue, isDisabled && styles.disabled)}>
|
{data.imgUrl ? (
|
||||||
{data.imgUrl ? (
|
<FadeWithImage
|
||||||
<FadeWithImage
|
loading={loading}
|
||||||
loading={loading}
|
imgUrl={data.imgUrl}
|
||||||
imgUrl={data.imgUrl}
|
styles={styles}
|
||||||
styles={styles}
|
alt={(data.label ?? data.value) as string}
|
||||||
alt={(data.label || data.value) as string}
|
/>
|
||||||
/>
|
) : (
|
||||||
) : (
|
<SlideOutTransition horizontal size={16} visible={loading} duration={150}>
|
||||||
<SlideOutTransition horizontal size={16} visible={loading} duration={150}>
|
<div className={styles.spinnerWrapper}>
|
||||||
<div className={styles.container}>
|
<Spinner className={styles.spinnerIcon} inline />
|
||||||
<Spinner className={styles.item} inline />
|
</div>
|
||||||
</div>
|
</SlideOutTransition>
|
||||||
</SlideOutTransition>
|
)}
|
||||||
)}
|
{!data.hideText && children}
|
||||||
{!data.hideText && children}
|
|
||||||
</div>
|
|
||||||
</components.SingleValue>
|
</components.SingleValue>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const FadeWithImage = (props: { loading: boolean; imgUrl: string; styles: StylesType; alt?: string }) => {
|
const FadeWithImage = (props: { loading: boolean; imgUrl: string; styles: StylesType; alt?: string }) => {
|
||||||
return (
|
return (
|
||||||
<div className={props.styles.container}>
|
<div className={props.styles.spinnerWrapper}>
|
||||||
<FadeTransition duration={150} visible={props.loading}>
|
<FadeTransition duration={150} visible={props.loading}>
|
||||||
<Spinner className={props.styles.item} inline />
|
<Spinner className={props.styles.spinnerIcon} inline />
|
||||||
</FadeTransition>
|
</FadeTransition>
|
||||||
<FadeTransition duration={150} visible={!props.loading}>
|
<FadeTransition duration={150} visible={!props.loading}>
|
||||||
<img className={props.styles.item} src={props.imgUrl} alt={props.alt} />
|
<img className={props.styles.spinnerIcon} src={props.imgUrl} alt={props.alt} />
|
||||||
</FadeTransition>
|
</FadeTransition>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -66,6 +66,7 @@ export const getSelectStyles = stylesFactory((theme: GrafanaTheme2) => {
|
|||||||
singleValue: css`
|
singleValue: css`
|
||||||
label: grafana-select-single-value;
|
label: grafana-select-single-value;
|
||||||
color: ${theme.components.input.text};
|
color: ${theme.components.input.text};
|
||||||
|
grid-area: 1 / 1 / 2 / 3;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
@ -75,7 +76,7 @@ export const getSelectStyles = stylesFactory((theme: GrafanaTheme2) => {
|
|||||||
valueContainer: css`
|
valueContainer: css`
|
||||||
label: grafana-select-value-container;
|
label: grafana-select-value-container;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
display: flex;
|
display: grid;
|
||||||
position: relative;
|
position: relative;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
flex: 1 1 0%;
|
flex: 1 1 0%;
|
||||||
@ -85,6 +86,7 @@ export const getSelectStyles = stylesFactory((theme: GrafanaTheme2) => {
|
|||||||
valueContainerMulti: css`
|
valueContainerMulti: css`
|
||||||
label: grafana-select-value-container-multi;
|
label: grafana-select-value-container-multi;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
display: flex;
|
||||||
`,
|
`,
|
||||||
loadingMessage: css`
|
loadingMessage: css`
|
||||||
label: grafana-select-loading-message;
|
label: grafana-select-loading-message;
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import { CSSObjectWithLabel } from 'react-select';
|
||||||
|
|
||||||
export default function resetSelectStyles() {
|
export default function resetSelectStyles() {
|
||||||
return {
|
return {
|
||||||
clearIndicator: () => ({}),
|
clearIndicator: () => ({}),
|
||||||
@ -8,7 +10,16 @@ export default function resetSelectStyles() {
|
|||||||
groupHeading: () => ({}),
|
groupHeading: () => ({}),
|
||||||
indicatorsContainer: () => ({}),
|
indicatorsContainer: () => ({}),
|
||||||
indicatorSeparator: () => ({}),
|
indicatorSeparator: () => ({}),
|
||||||
input: () => ({}),
|
input: function (originalStyles: CSSObjectWithLabel) {
|
||||||
|
return {
|
||||||
|
...originalStyles,
|
||||||
|
color: 'inherit',
|
||||||
|
margin: 0,
|
||||||
|
padding: 0,
|
||||||
|
// Set an explicit z-index here to ensure this element always overlays the singleValue
|
||||||
|
zIndex: 1,
|
||||||
|
};
|
||||||
|
},
|
||||||
loadingIndicator: () => ({}),
|
loadingIndicator: () => ({}),
|
||||||
loadingMessage: () => ({}),
|
loadingMessage: () => ({}),
|
||||||
menu: () => ({}),
|
menu: () => ({}),
|
||||||
|
Loading…
Reference in New Issue
Block a user