Skip to content
Snippets Groups Projects
Unverified Commit b0b8bc80 authored by Anton Kulyk's avatar Anton Kulyk Committed by GitHub
Browse files

Allow to unmap native model column from a real one (#20219)

* Add clear behavior to `SelectButton`

* Removed redundant type

* Allow unmapping model column from a real one
parent 89ae166d
No related branches found
No related tags found
No related merge requests found
......@@ -18,3 +18,14 @@ Default.args = {
hasValue: false,
fullWidth: false,
};
export const WithClearBehavior = Template.bind({});
WithClearBehavior.args = {
children: "Some value is selected",
hasValue: true,
onClear: () => {
return;
},
fullWidth: false,
};
import React, { ButtonHTMLAttributes, forwardRef } from "react";
import React, {
ButtonHTMLAttributes,
forwardRef,
useCallback,
useMemo,
} from "react";
import {
SelectButtonRoot,
......@@ -14,6 +19,7 @@ interface SelectButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
hasValue?: boolean;
disabled?: boolean;
fullWidth?: boolean;
onClear?: () => void;
}
const SelectButton = forwardRef(function SelectButton(
......@@ -25,10 +31,29 @@ const SelectButton = forwardRef(function SelectButton(
hasValue = true,
disabled,
fullWidth = true,
onClear,
...rest
}: SelectButtonProps,
ref,
) {
const handleClear = useCallback(
event => {
if (onClear) {
// Required not to trigger the usual SelectButton's onClick handler
event.stopPropagation();
onClear();
}
},
[onClear],
);
const rightIcon = useMemo(() => {
if (hasValue && onClear) {
return "close";
}
return "chevrondown";
}, [hasValue, onClear]);
return (
<SelectButtonRoot
type="button"
......@@ -45,7 +70,11 @@ const SelectButton = forwardRef(function SelectButton(
<SelectButtonContent data-testid="select-button-content">
{children}
</SelectButtonContent>
<SelectButtonIcon name="chevrondown" size={12} />
<SelectButtonIcon
name={rightIcon}
size={12}
onClick={onClear ? handleClear : undefined}
/>
</SelectButtonRoot>
);
});
......
......@@ -25,4 +25,46 @@ describe("SelectButton", () => {
expect(screen.getByRole("button")).not.toHaveFocus();
});
describe("clear behavior", () => {
it("should not display clear icon when the value is selected, but onClear prop is not provided", () => {
render(<SelectButton hasValue>{title}</SelectButton>);
expect(screen.queryByLabelText("close icon")).not.toBeInTheDocument();
});
it("should not display clear icon when the value is not selected", () => {
render(
<SelectButton hasValue={false} onClear={jest.fn()}>
{title}
</SelectButton>,
);
expect(screen.queryByLabelText("close icon")).not.toBeInTheDocument();
});
it("should call onClear when close icon is clicked", () => {
const onClear = jest.fn();
render(
<SelectButton hasValue onClear={onClear}>
{title}
</SelectButton>,
);
userEvent.click(screen.getByLabelText("close icon"));
expect(onClear).toHaveBeenCalledTimes(1);
});
it("should not call usual onClick when close icon is clicked", () => {
const onClick = jest.fn();
render(
<SelectButton hasValue onClick={onClick} onClear={jest.fn()}>
{title}
</SelectButton>,
);
userEvent.click(screen.getByLabelText("close icon"));
expect(onClick).not.toHaveBeenCalled();
});
});
});
import styled, { css } from "styled-components";
import { color } from "metabase/lib/colors";
import SelectButton from "metabase/core/components/SelectButton";
import { forwardRefToInnerRef } from "metabase/styled-components/utils";
export const StyledSelectButton = forwardRefToInnerRef(styled(SelectButton)`
${props =>
props.hasValue &&
css`
color: ${color("text-white")} !important;
background-color: ${color("brand")};
border-color: ${color("brand")};
.Icon {
color: ${color("text-white")};
}
`};
`);
......@@ -8,18 +8,12 @@ import { SchemaTableAndFieldDataSelector } from "metabase/query_builder/componen
import Field from "metabase-lib/lib/metadata/Field";
import Fields from "metabase/entities/fields";
import SelectButton from "metabase/core/components/SelectButton";
type CollapsedPickerProps = {
isTriggeredComponentOpen: boolean;
open: () => void;
close: () => void;
};
import { StyledSelectButton } from "./MappedFieldPicker.styled";
type MappedFieldPickerOwnProps = {
field: {
value: number | null;
onChange: (fieldId: number) => void;
onChange: (fieldId: number | null) => void;
};
formField: {
databaseId: number;
......@@ -74,15 +68,16 @@ function MappedFieldPicker({
? fieldObject.displayName({ includeTable: true })
: t`None`;
return (
<SelectButton
<StyledSelectButton
hasValue={!!fieldObject}
tabIndex={tabIndex}
ref={selectButtonRef}
onClear={() => onChange(null)}
>
{label}
</SelectButton>
</StyledSelectButton>
);
}, [fieldObject, tabIndex]);
}, [fieldObject, onChange, tabIndex]);
// DataSelector doesn't handle selectedTableId change prop nicely.
// During the initial load, fieldObject might have `table_id` set to `card__$ID` (retrieved from metadata)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment