mirror of
https://github.com/apache/superset.git
synced 2024-09-12 00:29:39 -04:00
feat: Labeled Error-bound Input (#14530)
* Error Input Form created * Adjusting styling * testing complete * Corrected component naming in Form * testing complete * Corrected component naming in Form * Renamed stories file to appropriate name * add image for alert * Fixed test * Switched from px to theme * Adjusting LabeledErrorBoundInputProps * validation now accepts a string Co-authored-by: Elizabeth Thompson <eschutho@gmail.com>
This commit is contained in:
parent
9729ffd7a1
commit
e4103c272e
@ -40,6 +40,7 @@ const RequiredLabel = styled.label`
|
|||||||
margin-bottom: ${({ theme }) => theme.gridUnit}px;
|
margin-bottom: ${({ theme }) => theme.gridUnit}px;
|
||||||
&::after {
|
&::after {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
margin-left: ${({ theme }) => theme.gridUnit}px;
|
||||||
color: ${({ theme }) => theme.colors.error.base};
|
color: ${({ theme }) => theme.colors.error.base};
|
||||||
font-size: ${({ theme }) => theme.typography.sizes.m}px;
|
font-size: ${({ theme }) => theme.typography.sizes.m}px;
|
||||||
content: '*';
|
content: '*';
|
||||||
|
@ -0,0 +1,79 @@
|
|||||||
|
/**
|
||||||
|
* 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, { useState } from 'react';
|
||||||
|
import LabeledErrorBoundInput, {
|
||||||
|
LabeledErrorBoundInputProps,
|
||||||
|
} from './LabeledErrorBoundInput';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'LabeledErrorBoundInput',
|
||||||
|
component: LabeledErrorBoundInput,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const InteractiveLabeledErrorBoundInput = ({
|
||||||
|
name,
|
||||||
|
value,
|
||||||
|
placeholder,
|
||||||
|
type,
|
||||||
|
id,
|
||||||
|
}: LabeledErrorBoundInputProps) => {
|
||||||
|
const [currentValue, setCurrentValue] = useState(value);
|
||||||
|
|
||||||
|
const validateFunctionality: (value: any) => string = value => {
|
||||||
|
setCurrentValue(value.target.value);
|
||||||
|
if (value.target.value.includes('success')) {
|
||||||
|
return 'success';
|
||||||
|
}
|
||||||
|
return 'error';
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<LabeledErrorBoundInput
|
||||||
|
id={id}
|
||||||
|
name={name}
|
||||||
|
validationMethods={{ onChange: validateFunctionality }}
|
||||||
|
errorMessage={
|
||||||
|
currentValue === 'success' ? '' : 'Type success in the text bar'
|
||||||
|
}
|
||||||
|
helpText="This is a line of example help text"
|
||||||
|
value={currentValue}
|
||||||
|
// This must stay the same as name or form breaks
|
||||||
|
label={name}
|
||||||
|
placeholder={placeholder}
|
||||||
|
type={type}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
InteractiveLabeledErrorBoundInput.args = {
|
||||||
|
name: 'Username',
|
||||||
|
placeholder: 'Example placeholder text...',
|
||||||
|
id: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
InteractiveLabeledErrorBoundInput.argTypes = {
|
||||||
|
type: {
|
||||||
|
defaultValue: 'textbox',
|
||||||
|
control: {
|
||||||
|
type: 'select',
|
||||||
|
options: ['textbox', 'checkbox', 'radio'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
@ -0,0 +1,61 @@
|
|||||||
|
/**
|
||||||
|
* 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 LabeledErrorBoundInput from 'src/components/Form/LabeledErrorBoundInput';
|
||||||
|
|
||||||
|
const defaultProps = {
|
||||||
|
id: 1,
|
||||||
|
label: 'Username',
|
||||||
|
name: 'Username',
|
||||||
|
validationMethods: () => {},
|
||||||
|
errorMessage: '',
|
||||||
|
helpText: 'This is a line of example help text',
|
||||||
|
value: '',
|
||||||
|
placeholder: 'Example placeholder text...',
|
||||||
|
type: 'textbox',
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('LabeledErrorBoundInput', () => {
|
||||||
|
it('renders a LabeledErrorBoundInput normally, without an error', () => {
|
||||||
|
render(<LabeledErrorBoundInput {...defaultProps} />);
|
||||||
|
|
||||||
|
const label = screen.getByText(/username/i);
|
||||||
|
const textboxInput = screen.getByRole('textbox');
|
||||||
|
const helperText = screen.getByText('This is a line of example help text');
|
||||||
|
|
||||||
|
expect(label).toBeVisible();
|
||||||
|
expect(textboxInput).toBeVisible();
|
||||||
|
expect(helperText).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders a LabeledErrorBoundInput with an error', () => {
|
||||||
|
// Pass an error into props, causing errorText to replace helperText
|
||||||
|
defaultProps.errorMessage = 'Example error message';
|
||||||
|
render(<LabeledErrorBoundInput {...defaultProps} />);
|
||||||
|
|
||||||
|
const label = screen.getByText(/username/i);
|
||||||
|
const textboxInput = screen.getByRole('textbox');
|
||||||
|
const errorText = screen.getByText(/example error message/i);
|
||||||
|
|
||||||
|
expect(label).toBeVisible();
|
||||||
|
expect(textboxInput).toBeVisible();
|
||||||
|
expect(errorText).toBeVisible();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,89 @@
|
|||||||
|
/**
|
||||||
|
* 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 { Input } from 'antd';
|
||||||
|
import { styled, css, SupersetTheme } from '@superset-ui/core';
|
||||||
|
import FormItem from './FormItem';
|
||||||
|
import FormLabel from './FormLabel';
|
||||||
|
|
||||||
|
export interface LabeledErrorBoundInputProps {
|
||||||
|
label?: string;
|
||||||
|
validationMethods:
|
||||||
|
| { onBlur: (value: any) => string }
|
||||||
|
| { onChange: (value: any) => string };
|
||||||
|
errorMessage: string | null;
|
||||||
|
helpText?: string;
|
||||||
|
required?: boolean;
|
||||||
|
id?: string;
|
||||||
|
[x: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
const StyledInput = styled(Input)`
|
||||||
|
margin: 8px 0;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const alertIconStyles = (theme: SupersetTheme, hasError: boolean) => css`
|
||||||
|
.ant-form-item-children-icon {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
${hasError &&
|
||||||
|
`.ant-form-item-control-input-content {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
content: ' ';
|
||||||
|
display: inline-block;
|
||||||
|
background: ${theme.colors.error.base};
|
||||||
|
mask: url('/images/icons/error.svg');
|
||||||
|
mask-size: cover;
|
||||||
|
width: ${theme.gridUnit * 4}px;
|
||||||
|
height: ${theme.gridUnit * 4}px;
|
||||||
|
position: absolute;
|
||||||
|
right: 7px;
|
||||||
|
top: 15px;
|
||||||
|
}
|
||||||
|
}`}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const LabeledErrorBoundInput = ({
|
||||||
|
label,
|
||||||
|
validationMethods,
|
||||||
|
errorMessage,
|
||||||
|
helpText,
|
||||||
|
required = false,
|
||||||
|
id,
|
||||||
|
...props
|
||||||
|
}: LabeledErrorBoundInputProps) => (
|
||||||
|
<>
|
||||||
|
<FormLabel htmlFor={id} required={required}>
|
||||||
|
{label}
|
||||||
|
</FormLabel>
|
||||||
|
<FormItem
|
||||||
|
css={(theme: SupersetTheme) => alertIconStyles(theme, !!errorMessage)}
|
||||||
|
validateTrigger={Object.keys(validationMethods)}
|
||||||
|
validateStatus={errorMessage ? 'error' : 'success'}
|
||||||
|
help={errorMessage || helpText}
|
||||||
|
hasFeedback={!!errorMessage}
|
||||||
|
>
|
||||||
|
<StyledInput {...props} {...validationMethods} />
|
||||||
|
</FormItem>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default LabeledErrorBoundInput;
|
@ -19,5 +19,6 @@
|
|||||||
import Form from './Form';
|
import Form from './Form';
|
||||||
import FormItem from './FormItem';
|
import FormItem from './FormItem';
|
||||||
import FormLabel from './FormLabel';
|
import FormLabel from './FormLabel';
|
||||||
|
import LabeledErrorBoundInput from './LabeledErrorBoundInput';
|
||||||
|
|
||||||
export { Form, FormItem, FormLabel };
|
export { Form, FormItem, FormLabel, LabeledErrorBoundInput };
|
||||||
|
Loading…
Reference in New Issue
Block a user