Skip to content

Commit

Permalink
fix: open dropdown popover correctly (#3594)
Browse files Browse the repository at this point in the history
* fix: dropdown popover opens correctly

* test: added component test for Dropdown component

* fix: remove empty selectedKey property
  • Loading branch information
chriskari authored Jan 16, 2025
1 parent fcfe6f4 commit 62e7294
Show file tree
Hide file tree
Showing 8 changed files with 187 additions and 43 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,6 @@ const getEnumComponent = (
return ({ onChange, setValue, onBlur, value, ...props }) => (
<Dropdown
{...props}
onKeyDown={event => {
event.preventDefault();
}}
value={value}
options={options}
setValue={v => {
Expand Down Expand Up @@ -78,7 +75,6 @@ export function KeyValuePairRenderer({
resource,
nestingLevel = 0,
editMode,
...props
}) {
// TODO the value obtained by ui-schema is undefined for this component
value = getObjectValueWorkaround(schema, resource, storeKeys, value);
Expand Down
7 changes: 3 additions & 4 deletions src/resources/Namespaces/MemoryQuotas.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export function MemoryInput({
setContainer,
required,
className,
...otherProps
disabled,
}) {
const units = ['K', 'Ki', 'M', 'Mi', 'G', 'Gi', 'Ti', 'T'];
const options = [
Expand All @@ -31,7 +31,6 @@ export function MemoryInput({
jp.value(container, propertyPath, val);
setContainer({ ...container });
};
if (!otherProps.readOnly) delete otherProps.readOnly;
return (
<FlexBox
direction="Column"
Expand All @@ -48,14 +47,14 @@ export function MemoryInput({
value={numericValue}
onInput={e => setValue(e.target.value + selectedUnit)}
className="full-width"
{...otherProps}
disabled={disabled}
/>
<Dropdown
options={options}
required={required}
selectedKey={selectedUnit}
onSelect={(_, { key }) => setValue(numericValue.toString() + key)}
{...otherProps}
disabled={disabled}
/>
</FlexBox>
</FlexBox>
Expand Down
3 changes: 0 additions & 3 deletions src/resources/Namespaces/NamespaceCreate.js
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,6 @@ export default function NamespaceCreate({
container={limits}
setContainer={setLimits}
propertyPath="$.spec.limits[0].max.memory"
enableResource={setWithLimits}
disabled={!withLimits}
required={withLimits}
/>
Expand All @@ -303,7 +302,6 @@ export default function NamespaceCreate({
container={limits}
setContainer={setLimits}
propertyPath="$.spec.limits[0].default.memory"
enableResource={setWithLimits}
disabled={!withLimits}
required={withLimits}
/>
Expand All @@ -312,7 +310,6 @@ export default function NamespaceCreate({
container={limits}
setContainer={setLimits}
propertyPath="$.spec.limits[0].defaultRequest.memory"
enableResource={setWithLimits}
disabled={!withLimits}
required={withLimits}
/>
Expand Down
11 changes: 4 additions & 7 deletions src/resources/Namespaces/Presets.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import React from 'react';
import { Presets } from 'shared/ResourceForm/components/Presets';
import { createResourceQuotaTemplate } from 'resources/ResourceQuotas/templates';
import { createLimitRangeTemplate } from 'resources/LimitRanges/templates';

export const LimitPresets = ({
presets,
value,
setValue,
namespaceName,
...otherProps
disabled,
}) => {
const mappedPresets = Object.entries(presets || {}).map(
([preset, values]) => {
Expand All @@ -32,17 +30,16 @@ export const LimitPresets = ({
presets={mappedPresets}
onSelect={preset => setValue(preset.value)}
inlinePresets={true}
{...otherProps}
disabled={disabled}
/>
) : null;
};

export const MemoryPresets = ({
presets,
value,
setValue,
namespaceName,
...otherProps
disabled,
}) => {
const mappedPresets = Object.entries(presets || {}).map(
([preset, values]) => {
Expand All @@ -65,7 +62,7 @@ export const MemoryPresets = ({
presets={mappedPresets}
onSelect={preset => setValue(preset.value)}
inlinePresets={true}
{...otherProps}
disabled={disabled}
/>
) : null;
};
7 changes: 2 additions & 5 deletions src/shared/ResourceForm/components/Presets.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import { useTranslation } from 'react-i18next';
import { Dropdown } from 'shared/components/Dropdown/Dropdown';

import './Presets.scss';

export function Presets({
presets,
onSelect,
inlinePresets = false,
...otherProps
disabled = false,
}) {
const { t } = useTranslation();
const options = presets.map(({ name }) => ({
Expand All @@ -20,14 +19,12 @@ export function Presets({
<Dropdown
placeholder={t('common.create-form.choose-template')} //TODO Have placeholder blank or sth
options={options}
selectedKey={''}
fullWidth={false}
disabled={disabled}
label={label}
onSelect={(e, preset) => {
e.stopPropagation();
onSelect(presets.find(p => p.name === preset.key));
}}
{...otherProps}
/>
);
return inlinePresets ? (
Expand Down
11 changes: 9 additions & 2 deletions src/shared/ResourceForm/inputs/Dropdown.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
import { Dropdown as BusolaDropdown } from 'shared/components/Dropdown/Dropdown';
import { useTranslation } from 'react-i18next';

export function Dropdown({ value, setValue, error, loading, ...props }) {
export function Dropdown({
value,
setValue,
error,
loading,
selectedKey,
...props
}) {
const { t } = useTranslation();

const getValidationState = () => {
Expand Down Expand Up @@ -29,7 +36,7 @@ export function Dropdown({ value, setValue, error, loading, ...props }) {
} = props;
return (
<BusolaDropdown
selectedKey={value}
selectedKey={selectedKey ?? value}
onSelect={(_, selected) => setValue(selected.key, selected)}
validationState={getValidationState()}
{...dropdownProps}
Expand Down
150 changes: 150 additions & 0 deletions src/shared/components/Dropdown/Dropdown.cy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
/* global cy, Cypress */
import { Dropdown } from './Dropdown';

describe('Dropdown Component', () => {
const defaultProps = {
id: 'test-dropdown',
label: 'Test Dropdown',
options: [
{ key: '1', text: 'Option 1' },
{ key: '2', text: 'Option 2' },
{ key: '3', text: 'Option 3' },
],
selectedKey: '1',
};

it('renders with required props', () => {
cy.mount(<Dropdown {...defaultProps} />);

cy.get('[data-testid="test-dropdown"]').should('exist');
cy.contains('Test Dropdown').should('exist');
});

it('displays the correct selected value', () => {
cy.mount(<Dropdown {...defaultProps} />);

cy.get('[data-testid="test-dropdown"]')
.shadow()
.find('input')
.should('have.value', 'Option 1');
});

it('handles option selection', () => {
const onSelect = cy.spy().as('onSelect');
cy.mount(<Dropdown {...defaultProps} onSelect={onSelect} />);

cy.get('[data-testid="test-dropdown"]').click();
cy.get('ui5-li')
.eq(1)
.click();

cy.get('@onSelect').should('have.been.calledOnce');
cy.get('@onSelect').should(
'have.been.calledWith',
Cypress.sinon.match.any,
{
key: '2',
text: 'Option 2',
},
);
});

it('displays empty list message when no options provided', () => {
const emptyProps = {
...defaultProps,
options: [],
emptyListMessage: 'Custom Empty Message',
};

cy.mount(<Dropdown {...emptyProps} />);

cy.get('[data-testid="test-dropdown"]')
.shadow()
.find('input')
.should('have.value', 'Custom Empty Message')
.should('be.disabled');
});

it('handles disabled state', () => {
cy.mount(<Dropdown {...defaultProps} disabled={true} />);

cy.get('[data-testid="test-dropdown"]')
.shadow()
.find('input')
.should('be.disabled');
});

it('sets autocomplete to off on focus', () => {
const onSelect = cy.spy().as('onSelect');
cy.mount(<Dropdown {...defaultProps} onSelect={onSelect} />);

cy.get('[data-testid="test-dropdown"]')
.shadow()
.find('input')
.focus()
.should('have.attr', 'autocomplete', 'off');
});

it('opens popover on focus', () => {
cy.mount(<Dropdown {...defaultProps} />);

cy.get('[data-testid="test-dropdown"]')
.shadow()
.find('input')
.focus();

cy.get('ui5-responsive-popover').should('have.attr', 'open');
});

it('opens popover on input click', () => {
cy.mount(<Dropdown {...defaultProps} />);

cy.get('[data-testid="test-dropdown"]')
.shadow()
.find('input')
.click();

cy.get('ui5-responsive-popover').should('have.attr', 'open');
});

it('opens popover on icon click', () => {
cy.mount(<Dropdown {...defaultProps} />);

cy.get('[data-testid="test-dropdown"]')
.shadow()
.find('ui5-icon[accessible-name="Select Options"]')
.click();

cy.get('ui5-responsive-popover').should('have.attr', 'open');
});

it('handles custom className prop', () => {
cy.mount(<Dropdown {...defaultProps} className="custom-class" />);

cy.get('[data-testid="test-dropdown"]').should(
'have.class',
'custom-class',
);
});

it('uses label as placeholder when no placeholder provided', () => {
const onSelect = cy.spy().as('onSelect');
cy.mount(<Dropdown {...defaultProps} onSelect={onSelect} />);

cy.get('[data-testid="test-dropdown"]').should(
'have.attr',
'placeholder',
'Test Dropdown',
);
});

it('uses custom placeholder when provided', () => {
cy.mount(<Dropdown {...defaultProps} placeholder="Custom Placeholder" />);

cy.get('[data-testid="test-dropdown"]').should(
'have.attr',
'placeholder',
'Custom Placeholder',
);
});
});
Loading

0 comments on commit 62e7294

Please sign in to comment.