mirror of
https://github.com/apache/superset.git
synced 2026-05-12 19:35:17 +00:00
chore: Improves the Select component UI/UX - iteration 4 (#15480)
This commit is contained in:
committed by
GitHub
parent
2aa889944d
commit
ad773ffe79
@@ -62,10 +62,13 @@ export function findValue<OptionType extends OptionTypeBase>(
|
||||
|
||||
export function hasOption(search: string, options: AntdOptionsType) {
|
||||
const searchOption = search.trim().toLowerCase();
|
||||
return options.find(
|
||||
opt =>
|
||||
opt.value.toLowerCase().includes(searchOption) ||
|
||||
(typeof opt.label === 'string' &&
|
||||
opt.label.toLowerCase().includes(searchOption)),
|
||||
);
|
||||
return options.find(opt => {
|
||||
const { label, value } = opt;
|
||||
const labelText = String(label);
|
||||
const valueText = String(value);
|
||||
return (
|
||||
valueText.toLowerCase().includes(searchOption) ||
|
||||
labelText.toLowerCase().includes(searchOption)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { render, screen } from 'spec/helpers/testing-library';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import fetchMock from 'fetch-mock';
|
||||
import SupersetResourceSelect from '.';
|
||||
|
||||
const mockedProps = {
|
||||
resource: 'dataset',
|
||||
searchColumn: 'table_name',
|
||||
onError: () => {},
|
||||
};
|
||||
|
||||
fetchMock.get('glob:*/api/v1/dataset/?q=*', {});
|
||||
|
||||
test('should render', () => {
|
||||
const { container } = render(<SupersetResourceSelect {...mockedProps} />);
|
||||
expect(container).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should render the Select... placeholder', () => {
|
||||
render(<SupersetResourceSelect {...mockedProps} />);
|
||||
expect(screen.getByText('Select...')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should render the Loading... message', () => {
|
||||
render(<SupersetResourceSelect {...mockedProps} />);
|
||||
const select = screen.getByText('Select...');
|
||||
userEvent.click(select);
|
||||
expect(screen.getByText('Loading...')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should render the No options message', async () => {
|
||||
render(<SupersetResourceSelect {...mockedProps} />);
|
||||
const select = screen.getByText('Select...');
|
||||
userEvent.click(select);
|
||||
expect(await screen.findByText('No options')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should render the typed text', async () => {
|
||||
render(<SupersetResourceSelect {...mockedProps} />);
|
||||
const select = screen.getByText('Select...');
|
||||
userEvent.click(select);
|
||||
userEvent.type(select, 'typed text');
|
||||
expect(await screen.findByText('typed text')).toBeInTheDocument();
|
||||
});
|
||||
@@ -1,121 +0,0 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import React, { useEffect } from 'react';
|
||||
import rison from 'rison';
|
||||
import { SupersetClient } from '@superset-ui/core';
|
||||
import { AsyncSelect } from 'src/components/Select';
|
||||
import {
|
||||
ClientErrorObject,
|
||||
getClientErrorObject,
|
||||
} from 'src/utils/getClientErrorObject';
|
||||
import { cacheWrapper } from 'src/utils/cacheWrapper';
|
||||
|
||||
export type Value<V> = { value: V; label: string };
|
||||
|
||||
export interface SupersetResourceSelectProps<T = unknown, V = string> {
|
||||
value?: Value<V> | null;
|
||||
initialId?: number | string;
|
||||
onChange?: (value?: Value<V>) => void;
|
||||
isMulti?: boolean;
|
||||
searchColumn?: string;
|
||||
resource?: string; // e.g. "dataset", "dashboard/related/owners"
|
||||
transformItem?: (item: T) => Value<V>;
|
||||
onError: (error: ClientErrorObject) => void;
|
||||
defaultOptions?: { value: number; label: string }[] | boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a special-purpose select component for when you're selecting
|
||||
* items from one of the standard Superset resource APIs.
|
||||
* Such as selecting a datasource, a chart, or users.
|
||||
*
|
||||
* If you're selecting a "related" resource (such as dashboard/related/owners),
|
||||
* leave the searchColumn prop unset.
|
||||
* The api doesn't do columns on related resources for some reason.
|
||||
*
|
||||
* If you're doing anything more complex than selecting a standard resource,
|
||||
* we'll all be better off if you use AsyncSelect directly instead.
|
||||
*/
|
||||
|
||||
const localCache = new Map<string, any>();
|
||||
|
||||
export const cachedSupersetGet = cacheWrapper(
|
||||
SupersetClient.get,
|
||||
localCache,
|
||||
({ endpoint }) => endpoint || '',
|
||||
);
|
||||
|
||||
export default function SupersetResourceSelect<T, V>({
|
||||
value,
|
||||
initialId,
|
||||
onChange,
|
||||
isMulti,
|
||||
resource,
|
||||
searchColumn,
|
||||
transformItem,
|
||||
onError,
|
||||
defaultOptions = true,
|
||||
}: SupersetResourceSelectProps<T, V>) {
|
||||
useEffect(() => {
|
||||
if (initialId == null) return;
|
||||
cachedSupersetGet({
|
||||
endpoint: `/api/v1/${resource}/${initialId}`,
|
||||
})
|
||||
.then(response => {
|
||||
const { result } = response.json;
|
||||
const value = transformItem ? transformItem(result) : result;
|
||||
if (onChange) onChange(value);
|
||||
})
|
||||
.catch(response => {
|
||||
if (response?.status === 404 && onChange) onChange(undefined);
|
||||
});
|
||||
}, [resource, initialId]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
function loadOptions(input: string) {
|
||||
const query = searchColumn
|
||||
? rison.encode({
|
||||
filters: [{ col: searchColumn, opr: 'ct', value: input }],
|
||||
})
|
||||
: rison.encode({ filter: value });
|
||||
return cachedSupersetGet({
|
||||
endpoint: `/api/v1/${resource}/?q=${query}`,
|
||||
}).then(
|
||||
response =>
|
||||
response.json.result
|
||||
.map(transformItem)
|
||||
.sort((a: Value<V>, b: Value<V>) => a.label.localeCompare(b.label)),
|
||||
async badResponse => {
|
||||
onError(await getClientErrorObject(badResponse));
|
||||
return [];
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<AsyncSelect
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
isMulti={isMulti}
|
||||
loadOptions={loadOptions}
|
||||
defaultOptions={defaultOptions} // true - load options on render
|
||||
cacheOptions
|
||||
filterOption={null} // options are filtered at the api
|
||||
/>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user