projection selector component (#10294)
This commit is contained in:
parent
f100b5918d
commit
3d8e605245
|
|
@ -21,7 +21,7 @@ import Tooltip from "components/ads/Tooltip";
|
|||
import { isEllipsisActive } from "utils/helpers";
|
||||
import SegmentHeader from "components/ads/ListSegmentHeader";
|
||||
import { useTheme } from "styled-components";
|
||||
import { findIndex } from "lodash";
|
||||
import { findIndex, isArray } from "lodash";
|
||||
|
||||
export type DropdownOnSelect = (value?: string, dropdownOption?: any) => void;
|
||||
|
||||
|
|
@ -252,7 +252,7 @@ const Selected = styled.div<{
|
|||
|
||||
export const DropdownContainer = styled.div<{ width: string; height?: string }>`
|
||||
width: ${(props) => props.width};
|
||||
height: ${(props) => props.height || `38px`};
|
||||
height: ${(props) => props.height || `auto`};
|
||||
position: relative;
|
||||
span.bp3-popover-target {
|
||||
display: inline-block;
|
||||
|
|
@ -272,6 +272,7 @@ const DropdownSelect = styled.div``;
|
|||
|
||||
export const DropdownWrapper = styled.div<{
|
||||
width: string;
|
||||
isOpen: boolean;
|
||||
}>`
|
||||
width: ${(props) => props.width};
|
||||
height: fit-content;
|
||||
|
|
@ -279,6 +280,8 @@ export const DropdownWrapper = styled.div<{
|
|||
background-color: ${(props) => props.theme.colors.dropdown.menu.bg};
|
||||
border: 1px solid ${(props) => props.theme.colors.dropdown.menu.border};
|
||||
padding: ${(props) => props.theme.spaces[3]}px 0;
|
||||
overflow: hidden;
|
||||
display: ${(props) => (props.isOpen ? "inline-block" : "none")};
|
||||
.dropdown-search {
|
||||
margin: 4px 12px 8px;
|
||||
width: calc(100% - 24px);
|
||||
|
|
@ -633,11 +636,13 @@ function DefaultDropDownValueNode({
|
|||
|
||||
interface DropdownOptionsProps extends DropdownProps, DropdownSearchProps {
|
||||
optionClickHandler: (option: DropdownOption) => void;
|
||||
selectedOptionClickHandler: (option: DropdownOption) => void;
|
||||
renderOption?: RenderOption;
|
||||
headerLabel?: string;
|
||||
selected: DropdownOption | DropdownOption[];
|
||||
optionWidth: string;
|
||||
isMultiSelect?: boolean;
|
||||
isOpen: boolean; // dropdown popover options flashes when closed, this prop helps to make sure it never happens again.
|
||||
}
|
||||
|
||||
export function RenderDropdownOptions(props: DropdownOptionsProps) {
|
||||
|
|
@ -663,6 +668,7 @@ export function RenderDropdownOptions(props: DropdownOptionsProps) {
|
|||
return (
|
||||
<DropdownWrapper
|
||||
className="ads-dropdown-options-wrapper"
|
||||
isOpen={props.isOpen}
|
||||
width={optionWidth}
|
||||
>
|
||||
{props.enableSearch && (
|
||||
|
|
@ -706,7 +712,12 @@ export function RenderDropdownOptions(props: DropdownOptionsProps) {
|
|||
aria-selected={isSelected}
|
||||
className="t--dropdown-option"
|
||||
key={index}
|
||||
onClick={() => props.optionClickHandler(option)}
|
||||
onClick={
|
||||
// users should be able to unselect a selected option by clicking the option again.
|
||||
isSelected
|
||||
? () => props.selectedOptionClickHandler(option)
|
||||
: () => props.optionClickHandler(option)
|
||||
}
|
||||
role="option"
|
||||
selected={isSelected}
|
||||
>
|
||||
|
|
@ -803,7 +814,7 @@ export default function Dropdown(props: DropdownProps) {
|
|||
(option: DropdownOption) => {
|
||||
if (props.isMultiSelect) {
|
||||
// Multi select -> typeof selected is array of objects
|
||||
if (!selected) {
|
||||
if (isArray(selected) && selected.length < 1) {
|
||||
setSelected([option]);
|
||||
} else {
|
||||
const newOptions: DropdownOption[] = [
|
||||
|
|
@ -942,7 +953,7 @@ export default function Dropdown(props: DropdownProps) {
|
|||
className={props.className}
|
||||
disabled={props.disabled}
|
||||
hasError={errorFlag}
|
||||
height={props.height || getMinHeight(props.isMultiSelect)}
|
||||
height={props.height || "38px"}
|
||||
isMultiSelect={props.isMultiSelect}
|
||||
isOpen={isOpen}
|
||||
onClick={() => setIsOpen(!isOpen)}
|
||||
|
|
@ -986,7 +997,7 @@ export default function Dropdown(props: DropdownProps) {
|
|||
<DropdownContainer
|
||||
className={props.containerClassName + " " + replayHighlightClass}
|
||||
data-cy={props.cypressSelector}
|
||||
height={getMinHeight(props.isMultiSelect)}
|
||||
height={"38px"}
|
||||
onKeyDown={handleKeydown}
|
||||
role="listbox"
|
||||
tabIndex={0}
|
||||
|
|
@ -1006,16 +1017,13 @@ export default function Dropdown(props: DropdownProps) {
|
|||
<RenderDropdownOptions
|
||||
{...props}
|
||||
isMultiSelect={props.isMultiSelect}
|
||||
isOpen={isOpen}
|
||||
optionClickHandler={optionClickHandler}
|
||||
optionWidth={dropdownOptionWidth}
|
||||
selected={selected ? selected : { id: undefined, value: undefined }}
|
||||
selectedOptionClickHandler={selectedOptionClickHandler}
|
||||
/>
|
||||
</Popover>
|
||||
</DropdownContainer>
|
||||
);
|
||||
}
|
||||
|
||||
function getMinHeight(isMultiSelect?: boolean): string {
|
||||
if (isMultiSelect) return "44px";
|
||||
return "38px";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,138 @@
|
|||
import React from "react";
|
||||
import BaseControl, { ControlProps } from "./BaseControl";
|
||||
import styled from "styled-components";
|
||||
import Dropdown, { DropdownOption } from "components/ads/Dropdown";
|
||||
import { ControlType } from "constants/PropertyControlConstants";
|
||||
import _ from "lodash";
|
||||
import { FieldArray, getFormValues } from "redux-form";
|
||||
import { connect } from "react-redux";
|
||||
import { AppState } from "reducers";
|
||||
import { QUERY_EDITOR_FORM_NAME } from "constants/forms";
|
||||
import { QueryAction } from "entities/Action";
|
||||
|
||||
const DropdownSelect = styled.div`
|
||||
font-size: 14px;
|
||||
width: 50vh;
|
||||
`;
|
||||
|
||||
class ProjectionSelectorControl extends BaseControl<
|
||||
ProjectionSelectorControlProps
|
||||
> {
|
||||
render() {
|
||||
let width = "50vh";
|
||||
if (this.props.customStyles && this.props?.customStyles?.width) {
|
||||
width = this.props?.customStyles?.width;
|
||||
}
|
||||
|
||||
return (
|
||||
<DropdownSelect data-cy={this.props.configProperty} style={{ width }}>
|
||||
<FieldArray
|
||||
component={renderDropdown}
|
||||
name={this.props.configProperty}
|
||||
options={this.props.options}
|
||||
props={{ ...this.props, width }}
|
||||
rerenderOnEveryChange={false}
|
||||
/>
|
||||
</DropdownSelect>
|
||||
);
|
||||
}
|
||||
|
||||
getControlType(): ControlType {
|
||||
return "PROJECTION";
|
||||
}
|
||||
}
|
||||
|
||||
function renderDropdown(props: any): JSX.Element {
|
||||
// values that have been selected and stored in redux form state.
|
||||
let selectedValues = props?.fields.getAll();
|
||||
if (_.isUndefined(selectedValues)) {
|
||||
selectedValues = props?.initialValue || [];
|
||||
}
|
||||
|
||||
// options that correspond to the selected values kept in redux form state.
|
||||
// the dropdown component requires the entire option object and not just the value.
|
||||
const selectedOptions = props.dropDownOptions.filter(
|
||||
(option: DropdownOption) => selectedValues.includes(option.value),
|
||||
);
|
||||
|
||||
// select option
|
||||
const onSelectOptions = (option: string | undefined) => {
|
||||
if (option) {
|
||||
props.fields.push(option);
|
||||
}
|
||||
};
|
||||
|
||||
// remove options
|
||||
const removeOption = (option: string | undefined) => {
|
||||
const options = selectedValues.map((field: DropdownOption) => field);
|
||||
const optionIndex = options.indexOf(option);
|
||||
|
||||
props.fields.remove(optionIndex);
|
||||
};
|
||||
|
||||
return (
|
||||
<Dropdown
|
||||
boundary="window"
|
||||
disabled={props.disabled}
|
||||
dontUsePortal={false}
|
||||
dropdownMaxHeight="250px"
|
||||
errorMsg={props.props?.errorText}
|
||||
helperText={props.props?.info}
|
||||
isMultiSelect
|
||||
onSelect={onSelectOptions}
|
||||
optionWidth="50vh"
|
||||
options={props.dropDownOptions}
|
||||
placeholder={props.props?.placeholderText || "Select options"}
|
||||
removeSelectedOption={removeOption}
|
||||
selected={selectedOptions}
|
||||
showLabelOnly
|
||||
width={props?.props?.width ? props?.props?.width : "50vh"}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export interface ProjectionSelectorControlProps extends ControlProps {
|
||||
options: DropdownOption[];
|
||||
placeholderText: string;
|
||||
propertyValue: string;
|
||||
subtitle?: string;
|
||||
isMultiSelect?: boolean;
|
||||
isSearchable?: boolean;
|
||||
fetchOptionsCondtionally?: boolean;
|
||||
}
|
||||
const mapStateToProps = (
|
||||
state: AppState,
|
||||
ownProps: ProjectionSelectorControlProps,
|
||||
) => {
|
||||
let dropDownOptions: DropdownOption[] = [];
|
||||
|
||||
// if the component has an option enabled to fetch the options dynamically,
|
||||
if (ownProps.fetchOptionsCondtionally) {
|
||||
// TODO: this is just a test, will be updated once the fetchDynamicFormData is implemented
|
||||
dropDownOptions = [
|
||||
{ label: "Test1", value: "SINGLE" },
|
||||
{ label: "Test2", value: "ALL" },
|
||||
];
|
||||
const dynamicFormDataString = _.get(
|
||||
getFormValues(QUERY_EDITOR_FORM_NAME)(state) as QueryAction,
|
||||
"actionConfiguration.formData.updateMany.query",
|
||||
);
|
||||
|
||||
// ownProps.configProperty will be used to filter from the array of data
|
||||
// const dynamicFormDataString = getFormEvaluationState(state);
|
||||
|
||||
try {
|
||||
dropDownOptions = JSON.parse(dynamicFormDataString);
|
||||
} catch (e) {
|
||||
dropDownOptions = [];
|
||||
}
|
||||
} else {
|
||||
dropDownOptions = ownProps.options;
|
||||
}
|
||||
|
||||
return {
|
||||
dropDownOptions,
|
||||
};
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps)(ProjectionSelectorControl);
|
||||
|
|
@ -42,6 +42,9 @@ import PaginationControl, {
|
|||
import SortingControl, {
|
||||
SortingControlProps,
|
||||
} from "components/formControls/SortingControl";
|
||||
import ProjectionSelectorControl, {
|
||||
ProjectionSelectorControlProps,
|
||||
} from "components/formControls/ProjectionSelectorControl";
|
||||
|
||||
class FormControlRegistry {
|
||||
static registerFormControlBuilders() {
|
||||
|
|
@ -132,6 +135,13 @@ class FormControlRegistry {
|
|||
return <SortingControl {...controlProps} />;
|
||||
},
|
||||
});
|
||||
FormControlFactory.registerControlBuilder("PROJECTION", {
|
||||
buildPropertyControl(
|
||||
controlProps: ProjectionSelectorControlProps,
|
||||
): JSX.Element {
|
||||
return <ProjectionSelectorControl {...controlProps} />;
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user