Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Select all for synchronous select #22084

Merged
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
0dfab9d
added select all button and a few tests
cccs-RyanK Oct 19, 2022
32e966d
Merge branch 'master' of github.com:CybercentreCanada/superset into s…
cccs-RyanK Nov 8, 2022
66af8c9
removed commented tests
cccs-RyanK Nov 8, 2022
6e7ac16
prettier fixes
cccs-RyanK Nov 8, 2022
4c599fd
lint fixes
cccs-RyanK Nov 10, 2022
06b77b0
added tests and feature for custom tag renderer
cccs-RyanK Nov 18, 2022
1efc58b
fixed onchange
cccs-RyanK Nov 23, 2022
42b45fd
Merge branch 'master' of github.com:CybercentreCanada/superset into s…
cccs-RyanK Nov 28, 2022
14129d0
fixed value format
cccs-RyanK Nov 29, 2022
3b596c6
Merge branch 'master' of github.com:CybercentreCanada/superset into s…
cccs-RyanK Nov 29, 2022
16e3c66
fixing wrong type issues
cccs-RyanK Nov 30, 2022
c69b7af
select test fixes
cccs-RyanK Nov 30, 2022
3d120d1
SqlEditor test changed with new select all behavior
cccs-RyanK Nov 30, 2022
6ca115e
fixed some lint errors
cccs-RyanK Dec 1, 2022
5702dd4
Merge branch 'master' of github.com:CybercentreCanada/superset into s…
cccs-RyanK Dec 14, 2022
c6c5f55
feedback suggestions
cccs-RyanK Dec 20, 2022
e36ceed
Merge branch 'master' of github.com:CybercentreCanada/superset into s…
cccs-RyanK Dec 20, 2022
426f57c
added tag renderer to async select
cccs-RyanK Dec 20, 2022
88f125e
fixed hook dependencies and ts error
Jan 12, 2023
807f51c
Merge branch 'master' of github.com:CybercentreCanada/superset into s…
Jan 12, 2023
618161f
removed custom max tag placeholder from select filter plugin
Jan 13, 2023
6b9b428
prettier fixes
Jan 16, 2023
0d66466
fixed bug in maxTagPlaceholder and value filter plugin tests
Jan 17, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions superset-frontend/src/components/Select/Select.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import React from 'react';
import { render, screen, waitFor, within } from 'spec/helpers/testing-library';
import userEvent from '@testing-library/user-event';
import { Select } from 'src/components';
import { SELECT_ALL_VALUE } from './utils';

const ARIA_LABEL = 'Test';
const NEW_OPTION = 'Kyle';
Expand Down Expand Up @@ -566,6 +567,13 @@ test('finds an element with a numeric value and does not duplicate the options',
expect(await querySelectOption('11')).not.toBeInTheDocument();
});

test('select all appears in options when multi select is true and selectAllEnabled is true', async () => {
cccs-RyanK marked this conversation as resolved.
Show resolved Hide resolved
render(<Select {...defaultProps} mode="multiple" selectAllEnabled />);
await open();
const options = await findAllSelectOptions();
expect(options[0]).toHaveTextContent(SELECT_ALL_VALUE);
});

/*
TODO: Add tests that require scroll interaction. Needs further investigation.
- Fetches more data when scrolling and more data is available
Expand Down
51 changes: 44 additions & 7 deletions superset-frontend/src/components/Select/Select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ import {
handleFilterOptionHelper,
dropDownRenderHelper,
getSuffixIcon,
labeledSelectAllOption,
SELECT_ALL_VALUE,
} from './utils';
import { SelectOptionsType, SelectProps } from './types';
import {
Expand Down Expand Up @@ -92,6 +94,8 @@ const Select = forwardRef(
options,
placeholder = t('Select ...'),
showSearch = true,
selectAllEnabled = false,
onSelectAll,
sortComparator = DEFAULT_SORT_COMPARATOR,
tokenSeparators,
value,
Expand All @@ -101,6 +105,7 @@ const Select = forwardRef(
ref: RefObject<HTMLInputElement>,
) => {
const isSingleMode = mode === 'single';
const isSelectAllMode = selectAllEnabled && !isSingleMode;
const shouldShowSearch = allowNewOptions ? true : showSearch;
const [selectValue, setSelectValue] = useState(value);
const [inputValue, setInputValue] = useState('');
Expand Down Expand Up @@ -149,10 +154,19 @@ const Select = forwardRef(
.map(opt =>
isLabeledValue(opt) ? opt : { value: opt, label: String(opt) },
);
return missingValues.length > 0
? missingValues.concat(selectOptions)
: selectOptions;
}, [selectOptions, selectValue]);
const fullOptions =
missingValues.length > 0
? missingValues.concat(selectOptions)
: selectOptions;

const fullOptionsWithSelectAll =
isSelectAllMode && !hasOption(getValue(SELECT_ALL_VALUE), missingValues)
? isLabeledValue(fullOptions[0])
? [labeledSelectAllOption, ...fullOptions]
: [labeledSelectAllOption.value, ...fullOptions]
: fullOptions;
return fullOptionsWithSelectAll;
}, [selectOptions, selectValue, isSelectAllMode]);

const handleOnSelect = (
selectedItem: string | number | AntdLabeledValue | undefined,
Expand All @@ -164,9 +178,26 @@ const Select = forwardRef(
const array = ensureIsArray(previousState);
const value = getValue(selectedItem);
// Tokenized values can contain duplicated values
if (value === getValue(SELECT_ALL_VALUE)) {
if (onSelectAll) {
onSelectAll();
}
const result = isLabeledValue(selectedItem)
? ([
labeledSelectAllOption,
...selectOptions,
] as AntdLabeledValue[])
: ([SELECT_ALL_VALUE, ...selectOptions] as (string | number)[]);
return result;
}
if (!hasOption(value, array)) {
const result = [...array, selectedItem];
return isLabeledValue(selectedItem)
return result.length === fullSelectOptions.length - 1 &&
isSelectAllMode
? isLabeledValue(selectedItem)
? ([labeledSelectAllOption, ...result] as AntdLabeledValue[])
: ([SELECT_ALL_VALUE, ...result] as (string | number)[])
: isLabeledValue(selectedItem)
? (result as AntdLabeledValue[])
: (result as (string | number)[]);
}
Expand All @@ -180,10 +211,16 @@ const Select = forwardRef(
value: string | number | AntdLabeledValue | undefined,
) => {
if (Array.isArray(selectValue)) {
if (isLabeledValue(value)) {
if (getValue(value) === getValue(SELECT_ALL_VALUE)) {
setSelectValue([]);
} else if (isLabeledValue(value)) {
const array = selectValue as AntdLabeledValue[];
setSelectValue(
array.filter(element => element.value !== value.value),
array.filter(
element =>
element.value !== value.value &&
element.value !== getValue(SELECT_ALL_VALUE),
),
);
} else {
const array = selectValue as (string | number)[];
Expand Down
10 changes: 10 additions & 0 deletions superset-frontend/src/components/Select/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,16 @@ export interface SelectProps extends BaseSelectProps {
* an array of options.
*/
options: SelectOptionsType;
/**
* It defines whether select all is an option.
* When selected all options are added to the selected
* values. Only available in sync select currently.
*/
selectAllEnabled: boolean;
cccs-RyanK marked this conversation as resolved.
Show resolved Hide resolved
/**
* Called when select all option is selected
*/
onSelectAll?: () => void;
cccs-RyanK marked this conversation as resolved.
Show resolved Hide resolved
}

export type AsyncSelectRef = HTMLInputElement & { clearCache: () => void };
Expand Down
6 changes: 6 additions & 0 deletions superset-frontend/src/components/Select/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ import { LabeledValue, RawValue, SelectOptionsType, V } from './types';

const { Option } = AntdSelect;

export const SELECT_ALL_VALUE = 'Select All';
cccs-RyanK marked this conversation as resolved.
Show resolved Hide resolved
export const labeledSelectAllOption: AntdLabeledValue = {
cccs-RyanK marked this conversation as resolved.
Show resolved Hide resolved
value: SELECT_ALL_VALUE,
label: String(SELECT_ALL_VALUE),
};

export function isObject(value: unknown): value is Record<string, unknown> {
return (
value !== null &&
Expand Down