fix: ChainedSelect 组件多展示一个空 Select (#6166)

This commit is contained in:
sansiro 2023-02-16 19:05:39 +08:00 committed by GitHub
parent bb57649d28
commit 980555d8dd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 50 additions and 143 deletions

View File

@ -2,4 +2,9 @@
.#{$ns}Select {
margin-right: var(--gap-xs);
}
&-spinner {
display: inline-block;
vertical-align: middle;
}
}

View File

@ -213,7 +213,7 @@ exports[`Renderer:chained-select 1`] = `
<div
aria-expanded="false"
aria-haspopup="listbox"
aria-labelledby="downshift-2-label"
aria-labelledby="downshift-1-label"
class="cxd-Select cxd-Select--inline"
role="combobox"
tabindex="0"
@ -239,7 +239,7 @@ exports[`Renderer:chained-select 1`] = `
<div
aria-expanded="false"
aria-haspopup="listbox"
aria-labelledby="downshift-3-label"
aria-labelledby="downshift-2-label"
class="cxd-Select cxd-Select--inline"
role="combobox"
tabindex="0"
@ -265,7 +265,7 @@ exports[`Renderer:chained-select 1`] = `
<div
aria-expanded="false"
aria-haspopup="listbox"
aria-labelledby="downshift-4-label"
aria-labelledby="downshift-3-label"
class="cxd-Select cxd-Select--inline"
role="combobox"
tabindex="0"
@ -289,128 +289,12 @@ exports[`Renderer:chained-select 1`] = `
</span>
</div>
<div
aria-expanded="true"
aria-haspopup="listbox"
aria-labelledby="downshift-5-label"
aria-owns="downshift-5-menu"
class="cxd-Select cxd-Select--inline is-opened has-popover"
role="combobox"
style="position: relative;"
tabindex="0"
class="cxd-Spinner in cxd-ChainedSelectControl-spinner"
data-testid="spinner"
>
<div
class="cxd-Select-valueWrap"
>
<div
class="cxd-Select-placeholder"
>
请选择
</div>
</div>
<span
class="cxd-Select-arrow"
>
<icon-mock
classname="icon icon-right-arrow-bold"
icon="right-arrow-bold"
/>
</span>
<div
class="cxd-PopOver cxd-Select-popover cxd-PopOver--leftBottomLeftTop"
style="display: block; width: 0px; left: 0px; top: 0px; position: relative;"
theme="cxd"
>
<div
class="cxd-PopOver-overlay"
/>
<div
class="cxd-Select-menu"
>
<div
class="cxd-Select-noResult"
>
未找到任何结果
</div>
</div>
<div
class="resize-sensor"
style="position: absolute; left: 0px; top: 0px; right: 0px; bottom: 0px; overflow: scroll; z-index: -1; visibility: hidden;"
>
<div
class="resize-sensor-expand"
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;"
>
<div
style="position: absolute; left: 0px; top: 0px; width: 10px; height: 10px;"
/>
</div>
<div
class="resize-sensor-shrink"
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;"
>
<div
style="position: absolute; left: 0; top: 0; width: 200%; height: 200%"
/>
</div>
<div
class="resize-sensor-appear"
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;animation-name: apearSensor; animation-duration: 0.2s;"
/>
</div>
</div>
<div
class="resize-sensor"
style="position: absolute; left: 0px; top: 0px; right: 0px; bottom: 0px; overflow: scroll; z-index: -1; visibility: hidden;"
>
<div
class="resize-sensor-expand"
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;"
>
<div
style="position: absolute; left: 0px; top: 0px; width: 10px; height: 10px;"
/>
</div>
<div
class="resize-sensor-shrink"
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;"
>
<div
style="position: absolute; left: 0; top: 0; width: 200%; height: 200%"
/>
</div>
<div
class="resize-sensor-appear"
style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;animation-name: apearSensor; animation-duration: 0.2s;"
/>
</div>
class="cxd-Spinner-icon cxd-Spinner-icon--sm cxd-Spinner-icon--default"
/>
</div>
</div>
</div>

View File

@ -116,8 +116,12 @@ test('Renderer:chained-select', async () => {
await waitFor(() => {
expect(fetcher).toBeCalledTimes(5);
});
fireEvent.click(getByText('请选择'));
expect(getByText('未找到任何结果')).toBeInTheDocument();
expect(container).not.toHaveTextContent('请选择');
expect(
container.querySelectorAll('.cxd-ChainedSelectControl > .cxd-Select')!
.length
).toBe(4);
expect(container).toMatchSnapshot();
});

View File

@ -7,7 +7,7 @@ import {
FormOptionsControl,
resolveEventData
} from 'amis-core';
import {Select} from 'amis-ui';
import {Select, Spinner} from 'amis-ui';
import {Api} from 'amis-core';
import {isEffectiveApi} from 'amis-core';
import {isMobile, createObject} from 'amis-core';
@ -15,6 +15,7 @@ import {ActionObject} from 'amis-core';
import {FormOptionsSchema} from '../../Schema';
import {supportStatic} from './StaticHoc';
import find from 'lodash/find';
import {isEmpty} from 'lodash';
/**
*
@ -36,13 +37,15 @@ export interface ChainedSelectProps
| 'inputClassName'
> {}
export interface StackItem {
options: Array<Option>;
parentId: any;
loading: boolean;
visible?: boolean;
}
export interface SelectState {
stack: Array<{
options: Array<Option>;
parentId: any;
loading: boolean;
visible?: boolean;
}>;
stack: Array<StackItem>;
}
export default class ChainedSelectControl extends React.Component<
@ -201,7 +204,7 @@ export default class ChainedSelectControl extends React.Component<
options,
parentId,
loading: false,
visible: !!options
visible: Array.isArray(options) && !isEmpty(options)
});
this.setState(
@ -268,16 +271,13 @@ export default class ChainedSelectControl extends React.Component<
delimiter
} = this.props;
const allOptions = [
{options, visible: true},
...(this.state.stack || [])
];
const allOptions = [{options, visible: true}, ...(this.state.stack || [])];
const valueArr = Array.isArray(value)
? value.concat()
: value && typeof value === 'string'
? value.split(delimiter || ',')
: [];
if (valueArr?.length > 0) {
displayValue = valueArr
.map((value: any, index) => {
@ -288,14 +288,19 @@ export default class ChainedSelectControl extends React.Component<
if (!options || !options.length) {
return value;
}
const selectedOption = find(options, (o) => value === o[valueField]) || {};
const selectedOption =
find(options, o => value === o[valueField]) || {};
return selectedOption[labelField] ?? value;
})
.filter(v => v != null)
.join(' > ');
}
return <div className={cx(`${classPrefix}SelectStaticControl`, className)}>{displayValue}</div>;
return (
<div className={cx(`${classPrefix}SelectStaticControl`, className)}>
{displayValue}
</div>
);
}
@supportStatic()
@ -322,6 +327,8 @@ export default class ChainedSelectControl extends React.Component<
? value.split(delimiter || ',')
: [];
const hasStackLoading = this.state.stack.find((a: StackItem) => a.loading);
const mobileUI = useMobileUI && isMobile();
return (
<div className={cx(`${ns}ChainedSelectControl`, className)}>
@ -343,7 +350,8 @@ export default class ChainedSelectControl extends React.Component<
/>
{this.state.stack.map(({options, loading, visible}, index) =>
visible === false ? null : (
// loading 中的选项不展示,避免没值再隐藏造成的闪烁,改用一个 Spinner 来展示 loading 状态
visible === false || loading ? null : (
<Select
{...rest}
useMobileUI={useMobileUI}
@ -357,11 +365,17 @@ export default class ChainedSelectControl extends React.Component<
options={Array.isArray(options) ? options : []}
value={arr[index + 1]}
onChange={this.handleChange.bind(this, index + 1)}
loading={loading}
inline
/>
)
)}
{hasStackLoading && (
<Spinner
size="sm"
className={cx(`${ns}ChainedSelectControl-spinner`)}
/>
)}
</div>
);
}