mirror of
https://github.com/apache/superset.git
synced 2026-05-09 18:05:52 +00:00
Add tests (#13778)
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
/**
|
||||
* 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 { fireEvent, render, screen } from 'spec/helpers/testing-library';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import TextControl from '.';
|
||||
|
||||
const mockedProps = {
|
||||
disabled: false,
|
||||
isFloat: false,
|
||||
isInt: false,
|
||||
placeholder: 'Placeholder',
|
||||
value: 'Sample value',
|
||||
};
|
||||
|
||||
test('should render', () => {
|
||||
const { container } = render(<TextControl {...mockedProps} />);
|
||||
expect(container).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should render a textbox', () => {
|
||||
render(<TextControl {...mockedProps} />);
|
||||
expect(screen.getByRole('textbox')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should render with initial value', () => {
|
||||
render(<TextControl {...mockedProps} />);
|
||||
expect(screen.getByDisplayValue('Sample value')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should render with placeholder', () => {
|
||||
render(<TextControl {...mockedProps} />);
|
||||
expect(screen.getByPlaceholderText('Placeholder')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should render as disabled', () => {
|
||||
const disabledProps = {
|
||||
...mockedProps,
|
||||
disabled: true,
|
||||
};
|
||||
render(<TextControl {...disabledProps} />);
|
||||
expect(screen.getByPlaceholderText('Placeholder')).toBeDisabled();
|
||||
});
|
||||
|
||||
test('should focus', () => {
|
||||
const focusProps = {
|
||||
...mockedProps,
|
||||
onFocus: jest.fn(),
|
||||
};
|
||||
render(<TextControl {...focusProps} />);
|
||||
const input = screen.getByPlaceholderText('Placeholder');
|
||||
fireEvent.focus(input);
|
||||
expect(focusProps.onFocus).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('should return errors when not a float', async () => {
|
||||
const changeProps = {
|
||||
...mockedProps,
|
||||
isFloat: true,
|
||||
value: null,
|
||||
onChange: jest.fn(),
|
||||
};
|
||||
render(<TextControl {...changeProps} />);
|
||||
const input = screen.getByPlaceholderText('Placeholder');
|
||||
await userEvent.type(input, '!num', { delay: 500 });
|
||||
expect(changeProps.onChange).toHaveBeenCalled();
|
||||
expect(changeProps.onChange).toHaveBeenCalledWith('!', [
|
||||
'is expected to be a number',
|
||||
]);
|
||||
});
|
||||
|
||||
test('should return errors when not an int', async () => {
|
||||
const changeProps = {
|
||||
...mockedProps,
|
||||
isInt: true,
|
||||
value: null,
|
||||
onChange: jest.fn(),
|
||||
};
|
||||
render(<TextControl {...changeProps} />);
|
||||
const input = screen.getByPlaceholderText('Placeholder');
|
||||
await userEvent.type(input, '!int', { delay: 500 });
|
||||
expect(changeProps.onChange).toHaveBeenCalled();
|
||||
expect(changeProps.onChange).toHaveBeenCalledWith('!', [
|
||||
'is expected to be an integer',
|
||||
]);
|
||||
});
|
||||
@@ -0,0 +1,126 @@
|
||||
/**
|
||||
* 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 { FormGroup, FormControl, FormControlProps } from 'react-bootstrap';
|
||||
import { legacyValidateNumber, legacyValidateInteger } from '@superset-ui/core';
|
||||
import debounce from 'lodash/debounce';
|
||||
import { FAST_DEBOUNCE } from 'src/constants';
|
||||
import ControlHeader from 'src/explore/components/ControlHeader';
|
||||
|
||||
type InputValueType = string | number;
|
||||
|
||||
export interface TextControlProps<T extends InputValueType = InputValueType> {
|
||||
disabled?: boolean;
|
||||
isFloat?: boolean;
|
||||
isInt?: boolean;
|
||||
onChange?: (value: T, errors: any) => {};
|
||||
onFocus?: () => {};
|
||||
placeholder?: string;
|
||||
value?: T | null;
|
||||
controlId?: string;
|
||||
renderTrigger?: boolean;
|
||||
}
|
||||
|
||||
export interface TextControlState {
|
||||
controlId: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
const generateControlId = (controlId?: string) =>
|
||||
`formInlineName_${controlId ?? (Math.random() * 1000000).toFixed()}`;
|
||||
|
||||
const safeStringify = (value?: InputValueType | null) =>
|
||||
value == null ? '' : String(value);
|
||||
|
||||
export default class TextControl<
|
||||
T extends InputValueType = InputValueType
|
||||
> extends React.Component<TextControlProps<T>, TextControlState> {
|
||||
initialValue?: TextControlProps['value'];
|
||||
|
||||
constructor(props: TextControlProps<T>) {
|
||||
super(props);
|
||||
this.initialValue = props.value;
|
||||
this.state = {
|
||||
// if there's no control id provided, generate a random
|
||||
// number to prevent rendering elements with same ids
|
||||
controlId: generateControlId(props.controlId),
|
||||
value: safeStringify(this.initialValue),
|
||||
};
|
||||
}
|
||||
|
||||
onChange = (inputValue: string) => {
|
||||
let parsedValue: InputValueType = inputValue;
|
||||
// Validation & casting
|
||||
const errors = [];
|
||||
if (inputValue !== '' && this.props.isFloat) {
|
||||
const error = legacyValidateNumber(inputValue);
|
||||
if (error) {
|
||||
errors.push(error);
|
||||
} else {
|
||||
parsedValue = inputValue.match(/.*([.0])$/g)
|
||||
? inputValue
|
||||
: parseFloat(inputValue);
|
||||
}
|
||||
}
|
||||
if (inputValue !== '' && this.props.isInt) {
|
||||
const error = legacyValidateInteger(inputValue);
|
||||
if (error) {
|
||||
errors.push(error);
|
||||
} else {
|
||||
parsedValue = parseInt(inputValue, 10);
|
||||
}
|
||||
}
|
||||
this.props.onChange?.(parsedValue as T, errors);
|
||||
};
|
||||
|
||||
debouncedOnChange = debounce((inputValue: string) => {
|
||||
this.onChange(inputValue);
|
||||
}, FAST_DEBOUNCE);
|
||||
|
||||
onChangeWrapper: FormControlProps['onChange'] = event => {
|
||||
const { value } = event.target as HTMLInputElement;
|
||||
this.setState({ value }, () => {
|
||||
this.debouncedOnChange(value);
|
||||
});
|
||||
};
|
||||
|
||||
render = () => {
|
||||
let { value } = this.state;
|
||||
if (this.initialValue !== this.props.value) {
|
||||
this.initialValue = this.props.value;
|
||||
value = safeStringify(this.props.value);
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<ControlHeader {...this.props} />
|
||||
<FormGroup controlId={this.state.controlId} bsSize="medium">
|
||||
<FormControl
|
||||
type="text"
|
||||
data-test="inline-name"
|
||||
placeholder={this.props.placeholder}
|
||||
onChange={this.onChangeWrapper}
|
||||
onFocus={this.props.onFocus}
|
||||
value={value}
|
||||
disabled={this.props.disabled}
|
||||
/>
|
||||
</FormGroup>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user