From 564e188fbe1f2876e021423d7df2a4938785307c Mon Sep 17 00:00:00 2001 From: spalger Date: Fri, 9 Sep 2022 19:16:03 -0500 Subject: [PATCH 01/10] [EuiForm] specify default fullWidth with root component --- scripts/jest/setup/throw_on_console_error.js | 6 ++- .../described_form_group.test.tsx.snap | 6 --- .../described_form_group.test.tsx | 23 +++++++++- .../described_form_group.tsx | 43 +++++++++-------- src/components/form/eui_form_context.ts | 21 +++++++++ .../form/field_number/field_number.test.tsx | 19 ++++++++ .../form/field_number/field_number.tsx | 46 +++++++++++-------- .../field_password/field_password.test.tsx | 21 +++++++++ .../form/field_password/field_password.tsx | 41 +++++++++-------- .../form/field_search/field_search.test.tsx | 19 ++++++++ .../form/field_search/field_search.tsx | 6 ++- .../form/field_text/field_text.test.tsx | 19 ++++++++ src/components/form/field_text/field_text.tsx | 40 ++++++++-------- .../form/file_picker/file_picker.test.tsx | 19 ++++++++ .../form/file_picker/file_picker.tsx | 7 ++- src/components/form/form.tsx | 18 +++++++- .../form_control_layout.test.tsx | 21 +++++++++ .../form_control_layout.tsx | 6 ++- .../__snapshots__/form_row.test.tsx.snap | 4 -- .../form/form_row/form_row.test.tsx | 19 ++++++++ src/components/form/form_row/form_row.tsx | 8 +++- .../__snapshots__/dual_range.test.tsx.snap | 34 ++++++++++++++ .../range/__snapshots__/range.test.tsx.snap | 26 +++++++++++ src/components/form/range/dual_range.test.tsx | 13 ++++++ src/components/form/range/dual_range.tsx | 7 ++- src/components/form/range/range.test.tsx | 13 ++++++ src/components/form/range/range.tsx | 7 ++- src/components/form/range/range_wrapper.tsx | 12 ++++- src/components/form/select/select.test.tsx | 15 ++++++ src/components/form/select/select.tsx | 41 +++++++++-------- .../super_select_control.test.tsx | 21 +++++++++ .../super_select/super_select_control.tsx | 39 ++++++++-------- .../form/text_area/text_area.test.tsx | 17 +++++++ src/components/form/text_area/text_area.tsx | 32 +++++++------ 34 files changed, 537 insertions(+), 152 deletions(-) create mode 100644 src/components/form/eui_form_context.ts diff --git a/scripts/jest/setup/throw_on_console_error.js b/scripts/jest/setup/throw_on_console_error.js index c6ab41954c1..c6d2464bdbe 100644 --- a/scripts/jest/setup/throw_on_console_error.js +++ b/scripts/jest/setup/throw_on_console_error.js @@ -1,6 +1,8 @@ +import { format } from 'util' + // Fail if a test ends up `console.error`-ing, e.g. if React logs because of a // failed prop types check. -console.error = (message) => { +console.error = (message, ...rest) => { // @see https://github.com/emotion-js/emotion/issues/1105 // This error that Emotion throws doesn't apply to Jest, so // we're just going to straight up ignore the first/nth-child warning @@ -14,5 +16,5 @@ console.error = (message) => { return; } - throw new Error(message); + throw new Error(format(message, ...rest)); }; diff --git a/src/components/form/described_form_group/__snapshots__/described_form_group.test.tsx.snap b/src/components/form/described_form_group/__snapshots__/described_form_group.test.tsx.snap index 1da4b5d8df1..8f9315731a9 100644 --- a/src/components/form/described_form_group/__snapshots__/described_form_group.test.tsx.snap +++ b/src/components/form/described_form_group/__snapshots__/described_form_group.test.tsx.snap @@ -40,7 +40,6 @@ exports[`EuiDescribedFormGroup is rendered 1`] = ` { expect(component).toMatchSnapshot(); }); }); + + describe('inherits', () => { + test('fullWidth from ', () => { + const component = render( + + + + ); + + if ( + !component + .find('.euiDescribedFormGroup') + .hasClass('euiDescribedFormGroup--fullWidth') + ) { + throw new Error( + 'expected EuiDescribedFormGroup to inherit fullWidth from EuiForm' + ); + } + }); + }); }); diff --git a/src/components/form/described_form_group/described_form_group.tsx b/src/components/form/described_form_group/described_form_group.tsx index 41e92631afd..a71635a637f 100644 --- a/src/components/form/described_form_group/described_form_group.tsx +++ b/src/components/form/described_form_group/described_form_group.tsx @@ -20,6 +20,7 @@ import { EuiFlexGroupGutterSize, EuiFlexItemProps, } from '../../flex'; +import { useFormContext } from '../eui_form_context'; export type EuiDescribedFormGroupProps = CommonProps & Omit, 'title'> & { @@ -33,6 +34,7 @@ export type EuiDescribedFormGroupProps = CommonProps & gutterSize?: EuiFlexGroupGutterSize; /** * Expand to fill 100% of the parent. + * Defaults to `fullWidth` prop of ``. * Default max-width is 800px. */ fullWidth?: boolean; @@ -64,19 +66,24 @@ export type EuiDescribedFormGroupProps = CommonProps & fieldFlexItemProps?: PropsOf; }; -export const EuiDescribedFormGroup: FunctionComponent = ({ - children, - className, - gutterSize = 'l', - fullWidth = false, - ratio = 'half', - titleSize = 'xs', - title, - description, - descriptionFlexItemProps, - fieldFlexItemProps, - ...rest -}) => { +export const EuiDescribedFormGroup: FunctionComponent = ( + props +) => { + const { defaultFullWidth } = useFormContext(); + + const { + children, + className, + gutterSize = 'l', + fullWidth = defaultFullWidth, + ratio = 'half', + titleSize = 'xs', + title, + description, + descriptionFlexItemProps, + fieldFlexItemProps, + ...rest + } = props; const classes = classNames( 'euiDescribedFormGroup', { @@ -93,18 +100,16 @@ export const EuiDescribedFormGroup: FunctionComponent{description}

; - } - renderedDescription = ( - {description} + { + // If the description is just a string, wrap it in a paragraph element + typeof description === 'string' ?

{description}

: description + }
); } diff --git a/src/components/form/eui_form_context.ts b/src/components/form/eui_form_context.ts new file mode 100644 index 00000000000..2b523d128a5 --- /dev/null +++ b/src/components/form/eui_form_context.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; + +export interface FormContextValue { + defaultFullWidth: boolean; +} + +export const FormContext = React.createContext({ + defaultFullWidth: false, +}); + +export function useFormContext() { + return React.useContext(FormContext); +} diff --git a/src/components/form/field_number/field_number.test.tsx b/src/components/form/field_number/field_number.test.tsx index 1a9e5ab2f6f..16b0fcb583e 100644 --- a/src/components/form/field_number/field_number.test.tsx +++ b/src/components/form/field_number/field_number.test.tsx @@ -10,6 +10,7 @@ import React from 'react'; import { render } from 'enzyme'; import { requiredProps } from '../../../test/required_props'; +import { EuiForm } from '../form'; import { EuiFieldNumber } from './field_number'; jest.mock('../form_control_layout', () => { @@ -89,4 +90,22 @@ describe('EuiFieldNumber', () => { }); }); }); + + describe('inherits', () => { + test('fullWidth from ', () => { + const component = render( + + + + ); + + if ( + !component.find('.euiFieldNumber').hasClass('euiFieldNumber--fullWidth') + ) { + throw new Error( + 'expected EuiFieldNumber to inherit fullWidth from EuiForm' + ); + } + }); + }); }); diff --git a/src/components/form/field_number/field_number.tsx b/src/components/form/field_number/field_number.tsx index c9dc16e25c8..07feafaed1c 100644 --- a/src/components/form/field_number/field_number.tsx +++ b/src/components/form/field_number/field_number.tsx @@ -18,6 +18,7 @@ import { import { EuiValidatableControl } from '../validatable_control'; import { IconType } from '../../icon'; +import { useFormContext } from '../eui_form_context'; import { getFormControlClassNameForIconCount } from '../form_control_layout/_num_icons'; export type EuiFieldNumberProps = Omit< @@ -64,26 +65,31 @@ export type EuiFieldNumberProps = Omit< compressed?: boolean; }; -export const EuiFieldNumber: FunctionComponent = ({ - className, - icon, - id, - placeholder, - name, - min, - max, - value, - isInvalid, - fullWidth = false, - isLoading = false, - compressed = false, - prepend, - append, - inputRef, - readOnly, - controlOnly, - ...rest -}) => { +export const EuiFieldNumber: FunctionComponent = ( + props +) => { + const { defaultFullWidth } = useFormContext(); + const { + className, + icon, + id, + placeholder, + name, + min, + max, + value, + isInvalid, + fullWidth = defaultFullWidth, + isLoading = false, + compressed = false, + prepend, + append, + inputRef, + readOnly, + controlOnly, + ...rest + } = props; + const numIconsClass = getFormControlClassNameForIconCount({ isInvalid, isLoading, diff --git a/src/components/form/field_password/field_password.test.tsx b/src/components/form/field_password/field_password.test.tsx index f05d195d39d..42dec19240f 100644 --- a/src/components/form/field_password/field_password.test.tsx +++ b/src/components/form/field_password/field_password.test.tsx @@ -10,6 +10,7 @@ import React from 'react'; import { render, mount } from 'enzyme'; import { requiredProps } from '../../../test/required_props'; +import { EuiForm } from '../form'; import { EuiFieldPassword, EuiFieldPasswordProps } from './field_password'; jest.mock('../validatable_control', () => ({ @@ -135,4 +136,24 @@ describe('EuiFieldPassword', () => { }); }); }); + + describe('inherits', () => { + test('fullWidth from ', () => { + const component = render( + + + + ); + + if ( + !component + .find('.euiFieldPassword') + .hasClass('euiFieldPassword--fullWidth') + ) { + throw new Error( + 'expected EuiFieldPassword to inherit fullWidth from EuiForm' + ); + } + }); + }); }); diff --git a/src/components/form/field_password/field_password.tsx b/src/components/form/field_password/field_password.tsx index 14ec5c65d8e..772e522de2e 100644 --- a/src/components/form/field_password/field_password.tsx +++ b/src/components/form/field_password/field_password.tsx @@ -25,6 +25,7 @@ import { EuiButtonIcon, EuiButtonIconPropsForButton } from '../../button'; import { useEuiI18n } from '../../i18n'; import { useCombinedRefs } from '../../../services'; import { getFormControlClassNameForIconCount } from '../form_control_layout/_num_icons'; +import { useFormContext } from '../eui_form_context'; export type EuiFieldPasswordProps = Omit< InputHTMLAttributes, @@ -63,23 +64,28 @@ export type EuiFieldPasswordProps = Omit< dualToggleProps?: Partial; }; -export const EuiFieldPassword: FunctionComponent = ({ - className, - id, - name, - placeholder, - value, - isInvalid, - fullWidth, - isLoading, - compressed, - inputRef: _inputRef, - prepend, - append, - type = 'password', - dualToggleProps, - ...rest -}) => { +export const EuiFieldPassword: FunctionComponent = ( + props +) => { + const { defaultFullWidth } = useFormContext(); + const { + className, + id, + name, + placeholder, + value, + isInvalid, + fullWidth = defaultFullWidth, + isLoading, + compressed, + inputRef: _inputRef, + prepend, + append, + type = 'password', + dualToggleProps, + ...rest + } = props; + // Set the initial input type to `password` if they want dual const [inputType, setInputType] = useState( type === 'dual' ? 'password' : type @@ -182,7 +188,6 @@ export const EuiFieldPassword: FunctionComponent = ({ EuiFieldPassword.defaultProps = { value: undefined, - fullWidth: false, isLoading: false, compressed: false, }; diff --git a/src/components/form/field_search/field_search.test.tsx b/src/components/form/field_search/field_search.test.tsx index 770af86f7c8..b818497352a 100644 --- a/src/components/form/field_search/field_search.test.tsx +++ b/src/components/form/field_search/field_search.test.tsx @@ -10,6 +10,7 @@ import React from 'react'; import { render } from 'enzyme'; import { requiredProps } from '../../../test/required_props'; +import { EuiForm } from '../form'; import { EuiFieldSearch } from './field_search'; jest.mock('../form_control_layout', () => ({ @@ -82,4 +83,22 @@ describe('EuiFieldSearch', () => { expect(component).toMatchSnapshot(); }); }); + + describe('inherits', () => { + test('fullWidth from ', () => { + const component = render( + + + + ); + + if ( + !component.find('.euiFieldSearch').hasClass('euiFieldSearch--fullWidth') + ) { + throw new Error( + 'expected EuiFieldSearch to inherit fullWidth from EuiForm' + ); + } + }); + }); }); diff --git a/src/components/form/field_search/field_search.tsx b/src/components/form/field_search/field_search.tsx index 0ddf39de8a2..525e34b3741 100644 --- a/src/components/form/field_search/field_search.tsx +++ b/src/components/form/field_search/field_search.tsx @@ -19,6 +19,7 @@ import { import { EuiValidatableControl } from '../validatable_control'; import { getFormControlClassNameForIconCount } from '../form_control_layout/_num_icons'; +import { FormContext, FormContextValue } from '../eui_form_context'; export interface EuiFieldSearchProps extends CommonProps, @@ -72,8 +73,8 @@ export class EuiFieldSearch extends Component< EuiFieldSearchProps, EuiFieldSearchState > { + static contextType = FormContext; static defaultProps = { - fullWidth: false, isLoading: false, incremental: false, compressed: false, @@ -197,13 +198,14 @@ export class EuiFieldSearch extends Component< }; render() { + const { defaultFullWidth } = this.context as FormContextValue; const { className, id, name, placeholder, isInvalid, - fullWidth, + fullWidth = defaultFullWidth, isLoading, inputRef, incremental, diff --git a/src/components/form/field_text/field_text.test.tsx b/src/components/form/field_text/field_text.test.tsx index 86fae243ef1..3928b38a50b 100644 --- a/src/components/form/field_text/field_text.test.tsx +++ b/src/components/form/field_text/field_text.test.tsx @@ -10,6 +10,7 @@ import React from 'react'; import { render } from 'enzyme'; import { requiredProps } from '../../../test/required_props'; +import { EuiForm } from '../form'; import { EuiFieldText } from './field_text'; jest.mock('../form_control_layout', () => { @@ -72,4 +73,22 @@ describe('EuiFieldText', () => { expect(component).toMatchSnapshot(); }); }); + + describe('inherits', () => { + test('fullWidth from ', () => { + const component = render( + + + + ); + + if ( + !component.find('.euiFieldText').hasClass('euiFieldText--fullWidth') + ) { + throw new Error( + 'expected EuiFieldText to inherit fullWidth from EuiForm' + ); + } + }); + }); }); diff --git a/src/components/form/field_text/field_text.tsx b/src/components/form/field_text/field_text.tsx index 2b38745560e..1c6e6a04c01 100644 --- a/src/components/form/field_text/field_text.tsx +++ b/src/components/form/field_text/field_text.tsx @@ -17,6 +17,7 @@ import { import { EuiValidatableControl } from '../validatable_control'; import { getFormControlClassNameForIconCount } from '../form_control_layout/_num_icons'; +import { useFormContext } from '../eui_form_context'; export type EuiFieldTextProps = InputHTMLAttributes & CommonProps & { @@ -51,24 +52,27 @@ export type EuiFieldTextProps = InputHTMLAttributes & compressed?: boolean; }; -export const EuiFieldText: FunctionComponent = ({ - id, - name, - placeholder, - value, - className, - icon, - isInvalid, - inputRef, - fullWidth = false, - isLoading, - compressed, - prepend, - append, - readOnly, - controlOnly, - ...rest -}) => { +export const EuiFieldText: FunctionComponent = (props) => { + const { defaultFullWidth } = useFormContext(); + const { + id, + name, + placeholder, + value, + className, + icon, + isInvalid, + inputRef, + fullWidth = defaultFullWidth, + isLoading, + compressed, + prepend, + append, + readOnly, + controlOnly, + ...rest + } = props; + const numIconsClass = getFormControlClassNameForIconCount({ isInvalid, isLoading, diff --git a/src/components/form/file_picker/file_picker.test.tsx b/src/components/form/file_picker/file_picker.test.tsx index 2c8f5002376..a199771570d 100644 --- a/src/components/form/file_picker/file_picker.test.tsx +++ b/src/components/form/file_picker/file_picker.test.tsx @@ -10,6 +10,7 @@ import React from 'react'; import { render } from 'enzyme'; import { requiredProps } from '../../../test'; +import { EuiForm } from '../form'; import { EuiFilePicker } from './file_picker'; describe('EuiFilePicker', () => { @@ -18,4 +19,22 @@ describe('EuiFilePicker', () => { expect(component).toMatchSnapshot(); }); + + describe('inherits', () => { + test('fullWidth from ', () => { + const component = render( + + + + ); + + if ( + !component.find('.euiFilePicker').hasClass('euiFilePicker--fullWidth') + ) { + throw new Error( + 'expected EuiFilePicker to inherit fullWidth from EuiForm' + ); + } + }); + }); }); diff --git a/src/components/form/file_picker/file_picker.tsx b/src/components/form/file_picker/file_picker.tsx index 6fe56f6f16c..80c7f5efdd4 100644 --- a/src/components/form/file_picker/file_picker.tsx +++ b/src/components/form/file_picker/file_picker.tsx @@ -18,6 +18,7 @@ import { EuiIcon } from '../../icon'; import { EuiI18n } from '../../i18n'; import { EuiLoadingSpinner } from '../../loading'; import { htmlIdGenerator } from '../../../services/accessibility'; +import { FormContext, FormContextValue } from '../eui_form_context'; const displayToClassNameMap = { default: null, @@ -59,6 +60,8 @@ export interface EuiFilePickerProps } export class EuiFilePicker extends Component { + static contextType = FormContext; + static defaultProps = { initialPromptText: ( { }; render() { + const { defaultFullWidth } = this.context as FormContextValue; + return ( { compressed, onChange, isInvalid, - fullWidth, + fullWidth = defaultFullWidth, isLoading, display, ...rest diff --git a/src/components/form/form.tsx b/src/components/form/form.tsx index 3944cf4dd54..ae101ff85d8 100644 --- a/src/components/form/form.tsx +++ b/src/components/form/form.tsx @@ -18,6 +18,8 @@ import { EuiCallOut } from '../call_out'; import { EuiI18n } from '../i18n'; import { CommonProps, ExclusiveUnion } from '../common'; +import { FormContext, FormContextValue } from './eui_form_context'; + export type EuiFormProps = CommonProps & ExclusiveUnion< { component: 'form' } & FormHTMLAttributes, @@ -33,6 +35,8 @@ export type EuiFormProps = CommonProps & * Where to display the callout with the list of errors */ invalidCallout?: 'above' | 'none'; + /** default `fullWidth` prop for children of this form */ + fullWidth?: boolean; }; export const EuiForm = forwardRef( @@ -44,10 +48,18 @@ export const EuiForm = forwardRef( error, component = 'div', invalidCallout = 'above', + fullWidth, ...rest }, ref ) => { + const formContext = React.useMemo( + (): FormContextValue => ({ + defaultFullWidth: fullWidth ?? false, + }), + [fullWidth] + ); + const handleFocus = useCallback((node) => { node?.focus(); }, []); @@ -103,8 +115,10 @@ export const EuiForm = forwardRef( className={classes} {...(rest as HTMLAttributes)} > - {optionalErrorAlert} - {children} + + {optionalErrorAlert} + {children} + ); } diff --git a/src/components/form/form_control_layout/form_control_layout.test.tsx b/src/components/form/form_control_layout/form_control_layout.test.tsx index 716032c3a98..5e94e108aa1 100644 --- a/src/components/form/form_control_layout/form_control_layout.test.tsx +++ b/src/components/form/form_control_layout/form_control_layout.test.tsx @@ -11,6 +11,7 @@ import { render, mount } from 'enzyme'; import { findTestSubject, requiredProps } from '../../../test'; +import { EuiForm } from '../form'; import { EuiFormControlLayout, ICON_SIDES } from './form_control_layout'; jest.mock('../../', () => ({ @@ -209,4 +210,24 @@ describe('EuiFormControlLayout', () => { expect(component).toMatchSnapshot(); }); }); + + describe('inherits', () => { + test('fullWidth from ', () => { + const component = render( + + + + ); + + if ( + !component + .find('.euiFormControlLayout') + .hasClass('euiFormControlLayout--fullWidth') + ) { + throw new Error( + 'expected EuiFormControlLayout to inherit fullWidth from EuiForm' + ); + } + }); + }); }); diff --git a/src/components/form/form_control_layout/form_control_layout.tsx b/src/components/form/form_control_layout/form_control_layout.tsx index 67ceeef2737..827a81cd628 100644 --- a/src/components/form/form_control_layout/form_control_layout.tsx +++ b/src/components/form/form_control_layout/form_control_layout.tsx @@ -21,6 +21,7 @@ import { } from './form_control_layout_icons'; import { CommonProps } from '../../common'; import { EuiFormLabel } from '../form_label'; +import { FormContext, FormContextValue } from '../eui_form_context'; export { ICON_SIDES } from './form_control_layout_icons'; @@ -60,12 +61,15 @@ export type EuiFormControlLayoutProps = CommonProps & }; export class EuiFormControlLayout extends Component { + static contextType = FormContext; + render() { + const { defaultFullWidth } = this.context as FormContextValue; const { children, icon, clear, - fullWidth, + fullWidth = defaultFullWidth, isLoading, isDisabled, compressed, diff --git a/src/components/form/form_row/__snapshots__/form_row.test.tsx.snap b/src/components/form/form_row/__snapshots__/form_row.test.tsx.snap index d077093a9ca..edf0e0a08fb 100644 --- a/src/components/form/form_row/__snapshots__/form_row.test.tsx.snap +++ b/src/components/form/form_row/__snapshots__/form_row.test.tsx.snap @@ -4,7 +4,6 @@ exports[`EuiFormRow behavior onBlur is called in child 1`] = ` { @@ -293,4 +294,22 @@ describe('EuiFormRow', () => { }); }); }); + + describe('inherits', () => { + test('fullWidth from ', () => { + const component = render( + + Label}> + + + + ); + + if (!component.find('.euiFormRow').hasClass('euiFormRow--fullWidth')) { + throw new Error( + 'expected EuiFormRow to inherit fullWidth from EuiForm' + ); + } + }); + }); }); diff --git a/src/components/form/form_row/form_row.tsx b/src/components/form/form_row/form_row.tsx index 84c85a76edd..fdf5b09ec43 100644 --- a/src/components/form/form_row/form_row.tsx +++ b/src/components/form/form_row/form_row.tsx @@ -22,6 +22,7 @@ import { get } from '../../../services/objects'; import { EuiFormHelpText } from '../form_help_text'; import { EuiFormErrorText } from '../form_error_text'; import { EuiFormLabel } from '../form_label'; +import { FormContext, FormContextValue } from '../eui_form_context'; import { htmlIdGenerator } from '../../../services/accessibility'; @@ -106,10 +107,11 @@ type LegendProps = { export type EuiFormRowProps = ExclusiveUnion; export class EuiFormRow extends Component { + static contextType = FormContext; + static defaultProps = { display: 'row', hasEmptyLabelSpace: false, - fullWidth: false, describedByIds: [], labelType: 'label', hasChildLabel: true, @@ -151,6 +153,8 @@ export class EuiFormRow extends Component { }; render() { + const { defaultFullWidth } = this.context as FormContextValue; + const { children, helpText, @@ -160,7 +164,7 @@ export class EuiFormRow extends Component { labelType, labelAppend, hasEmptyLabelSpace, - fullWidth, + fullWidth = defaultFullWidth, className, describedByIds, display, diff --git a/src/components/form/range/__snapshots__/dual_range.test.tsx.snap b/src/components/form/range/__snapshots__/dual_range.test.tsx.snap index 2589c2a13ed..57f209be943 100644 --- a/src/components/form/range/__snapshots__/dual_range.test.tsx.snap +++ b/src/components/form/range/__snapshots__/dual_range.test.tsx.snap @@ -52,6 +52,40 @@ exports[`EuiDualRange allows value prop to accept numbers 1`] = ` `; +exports[`EuiDualRange inherits fullWidth from 1`] = ` +
+
+
+ +
+
+
+
+
+
+`; + exports[`EuiDualRange input props can be applied to min and max inputs 1`] = `
`; +exports[`EuiRange inherits fullWidth from 1`] = ` +
+
+
+ +
+
+
+`; + exports[`EuiRange is rendered 1`] = `
{ expect(component).toMatchSnapshot(); }); }); + + describe('inherits', () => { + test('fullWidth from ', () => { + const component = render( + + {}} /> + + ); + + expect(component).toMatchSnapshot(); + }); + }); }); diff --git a/src/components/form/range/dual_range.tsx b/src/components/form/range/dual_range.tsx index 66b0b5ed001..16f955e5ecd 100644 --- a/src/components/form/range/dual_range.tsx +++ b/src/components/form/range/dual_range.tsx @@ -31,6 +31,7 @@ import { EuiRangeTick } from './range_ticks'; import { EuiRangeTrack } from './range_track'; import { EuiRangeWrapper } from './range_wrapper'; import { calculateThumbPosition } from './utils'; +import { FormContext, FormContextValue } from '../eui_form_context'; type ValueMember = number | string; @@ -108,11 +109,12 @@ export interface EuiDualRangeProps } export class EuiDualRange extends Component { + static contextType = FormContext; + static defaultProps = { min: 0, max: 100, step: 1, - fullWidth: false, compressed: false, isLoading: false, showLabels: false, @@ -499,11 +501,12 @@ export class EuiDualRange extends Component { }; render() { + const { defaultFullWidth } = this.context as FormContextValue; const { className, compressed, disabled, - fullWidth, + fullWidth = defaultFullWidth, isLoading, readOnly, id: propsId, diff --git a/src/components/form/range/range.test.tsx b/src/components/form/range/range.test.tsx index 5f464629b1a..d8a3c6d1d62 100644 --- a/src/components/form/range/range.test.tsx +++ b/src/components/form/range/range.test.tsx @@ -10,6 +10,7 @@ import React from 'react'; import { render } from 'enzyme'; import { requiredProps } from '../../../test/required_props'; +import { EuiForm } from '../form'; import { EuiRange } from './range'; const props = { @@ -186,4 +187,16 @@ describe('EuiRange', () => { expect(component).toMatchSnapshot(); }); + + describe('inherits', () => { + test('fullWidth from ', () => { + const component = render( + + + + ); + + expect(component).toMatchSnapshot(); + }); + }); }); diff --git a/src/components/form/range/range.tsx b/src/components/form/range/range.tsx index b2e771955e8..2b49ee1810e 100644 --- a/src/components/form/range/range.tsx +++ b/src/components/form/range/range.tsx @@ -23,6 +23,7 @@ import { EuiRangeTick } from './range_ticks'; import { EuiRangeTooltip } from './range_tooltip'; import { EuiRangeTrack } from './range_track'; import { EuiRangeWrapper } from './range_wrapper'; +import { FormContext, FormContextValue } from '../eui_form_context'; export interface EuiRangeProps extends CommonProps, @@ -87,11 +88,12 @@ export interface EuiRangeProps } export class EuiRange extends Component { + static contextType = FormContext; + static defaultProps = { min: 0, max: 100, step: 1, - fullWidth: false, compressed: false, isLoading: false, showLabels: false, @@ -163,11 +165,12 @@ export class EuiRange extends Component { }; render() { + const { defaultFullWidth } = this.context as FormContextValue; const { className, compressed, disabled, - fullWidth, + fullWidth = defaultFullWidth, isLoading, readOnly, id: propsId, diff --git a/src/components/form/range/range_wrapper.tsx b/src/components/form/range/range_wrapper.tsx index 59703447c75..bf20a88bbe4 100644 --- a/src/components/form/range/range_wrapper.tsx +++ b/src/components/form/range/range_wrapper.tsx @@ -9,6 +9,7 @@ import React, { HTMLAttributes, forwardRef } from 'react'; import classNames from 'classnames'; import { CommonProps } from '../../common'; +import { useFormContext } from '../eui_form_context'; export interface EuiRangeWrapperProps extends CommonProps, @@ -18,7 +19,16 @@ export interface EuiRangeWrapperProps } export const EuiRangeWrapper = forwardRef( - ({ children, className, fullWidth, compressed, ...rest }, ref) => { + (props, ref) => { + const { defaultFullWidth } = useFormContext(); + const { + children, + className, + fullWidth = defaultFullWidth, + compressed, + ...rest + } = props; + const classes = classNames( 'euiRangeWrapper', { diff --git a/src/components/form/select/select.test.tsx b/src/components/form/select/select.test.tsx index 21eff6387bc..0598df5a916 100644 --- a/src/components/form/select/select.test.tsx +++ b/src/components/form/select/select.test.tsx @@ -11,6 +11,7 @@ import React from 'react'; import { render, mount } from 'enzyme'; import { requiredProps } from '../../../test/required_props'; +import { EuiForm } from '../form'; import { EuiSelect } from './select'; jest.mock('../form_control_layout', () => ({ @@ -153,4 +154,18 @@ describe('EuiSelect', () => { ).toBe(''); }); }); + + describe('inherits', () => { + test('fullWidth from ', () => { + const component = render( + + + + ); + + if (!component.find('.euiSelect').hasClass('euiSelect--fullWidth')) { + throw new Error('expected EuiSelect to inherit fullWidth from EuiForm'); + } + }); + }); }); diff --git a/src/components/form/select/select.tsx b/src/components/form/select/select.tsx index fff991f9d44..1d280cd3a95 100644 --- a/src/components/form/select/select.tsx +++ b/src/components/form/select/select.tsx @@ -19,6 +19,7 @@ import { EuiFormControlLayoutProps, } from '../form_control_layout'; import { EuiValidatableControl } from '../validatable_control'; +import { useFormContext } from '../eui_form_context'; import { getFormControlClassNameForIconCount } from '../form_control_layout/_num_icons'; export interface EuiSelectOption @@ -60,25 +61,27 @@ export type EuiSelectProps = Omit< append?: EuiFormControlLayoutProps['append']; }; -export const EuiSelect: FunctionComponent = ({ - className, - options = [], - id, - name, - inputRef, - isInvalid, - fullWidth = false, - isLoading = false, - hasNoInitialSelection = false, - defaultValue, - compressed = false, - value: _value, - prepend, - append, - onMouseUp, - disabled, - ...rest -}) => { +export const EuiSelect: FunctionComponent = (props) => { + const { defaultFullWidth } = useFormContext(); + const { + className, + options = [], + id, + name, + inputRef, + isInvalid, + fullWidth = defaultFullWidth, + isLoading = false, + hasNoInitialSelection = false, + defaultValue, + compressed = false, + value: _value, + prepend, + append, + onMouseUp, + disabled, + ...rest + } = props; // if this is injecting an empty option for `hasNoInitialSelection` then // value needs to fallback to an empty string to interact properly with `defaultValue` const value = hasNoInitialSelection ? _value ?? '' : _value; diff --git a/src/components/form/super_select/super_select_control.test.tsx b/src/components/form/super_select/super_select_control.test.tsx index f3b85b4705f..d849e262b9c 100644 --- a/src/components/form/super_select/super_select_control.test.tsx +++ b/src/components/form/super_select/super_select_control.test.tsx @@ -10,6 +10,7 @@ import React from 'react'; import { render } from 'enzyme'; import { requiredProps } from '../../../test'; +import { EuiForm } from '../form'; import { EuiSuperSelectControl } from './super_select_control'; describe('EuiSuperSelectControl', () => { @@ -88,4 +89,24 @@ describe('EuiSuperSelectControl', () => { expect(component).toMatchSnapshot(); }); }); + + describe('inherits', () => { + test('fullWidth from ', () => { + const component = render( + + + + ); + + if ( + !component + .find('.euiSuperSelectControl') + .hasClass('euiSuperSelectControl--fullWidth') + ) { + throw new Error( + 'expected EuiSuperSelectControl to inherit fullWidth from EuiForm' + ); + } + }); + }); }); diff --git a/src/components/form/super_select/super_select_control.tsx b/src/components/form/super_select/super_select_control.tsx index 3eecc161f54..283b3d4e554 100644 --- a/src/components/form/super_select/super_select_control.tsx +++ b/src/components/form/super_select/super_select_control.tsx @@ -23,6 +23,7 @@ import { } from '../form_control_layout'; import { EuiI18n } from '../../i18n'; import { getFormControlClassNameForIconCount } from '../form_control_layout/_num_icons'; +import { useFormContext } from '../eui_form_context'; export interface EuiSuperSelectOption { value: T; @@ -66,24 +67,26 @@ export interface EuiSuperSelectControlProps export const EuiSuperSelectControl: ( props: EuiSuperSelectControlProps -) => ReturnType>> = ({ - className, - options = [], - id, - name, - fullWidth = false, - isLoading = false, - isInvalid = false, - readOnly, - defaultValue, - compressed = false, - value, - prepend, - append, - screenReaderId, - disabled, - ...rest -}) => { +) => ReturnType>> = (props) => { + const { defaultFullWidth } = useFormContext(); + const { + className, + options = [], + id, + name, + fullWidth = defaultFullWidth, + isLoading = false, + isInvalid = false, + readOnly, + defaultValue, + compressed = false, + value, + prepend, + append, + screenReaderId, + disabled, + ...rest + } = props; const numIconsClass = getFormControlClassNameForIconCount({ isInvalid, isLoading, diff --git a/src/components/form/text_area/text_area.test.tsx b/src/components/form/text_area/text_area.test.tsx index a01cdc6425e..e37622241a1 100644 --- a/src/components/form/text_area/text_area.test.tsx +++ b/src/components/form/text_area/text_area.test.tsx @@ -10,6 +10,7 @@ import React from 'react'; import { render } from 'enzyme'; import { requiredProps } from '../../../test/required_props'; +import { EuiForm } from '../form'; import { EuiTextArea } from './text_area'; describe('EuiTextArea', () => { @@ -18,4 +19,20 @@ describe('EuiTextArea', () => { expect(component).toMatchSnapshot(); }); + + describe('inherits', () => { + test('fullWidth from ', () => { + const component = render( + + + + ); + + if (!component.find('.euiTextArea').hasClass('euiTextArea--fullWidth')) { + throw new Error( + 'expected EuiTextArea to inherit fullWidth from EuiForm' + ); + } + }); + }); }); diff --git a/src/components/form/text_area/text_area.tsx b/src/components/form/text_area/text_area.tsx index 36123cb4acc..648c0bc25e8 100644 --- a/src/components/form/text_area/text_area.tsx +++ b/src/components/form/text_area/text_area.tsx @@ -10,6 +10,7 @@ import React, { TextareaHTMLAttributes, Ref, FunctionComponent } from 'react'; import { CommonProps } from '../../common'; import classNames from 'classnames'; import { EuiValidatableControl } from '../validatable_control'; +import { useFormContext } from '../eui_form_context'; export type EuiTextAreaProps = TextareaHTMLAttributes & CommonProps & { @@ -34,20 +35,23 @@ const resizeToClassNameMap = { export const RESIZE = Object.keys(resizeToClassNameMap); -export const EuiTextArea: FunctionComponent = ({ - children, - className, - compressed, - fullWidth = false, - id, - inputRef, - isInvalid, - name, - placeholder, - resize = 'vertical', - rows, - ...rest -}) => { +export const EuiTextArea: FunctionComponent = (props) => { + const { defaultFullWidth } = useFormContext(); + const { + children, + className, + compressed, + fullWidth = defaultFullWidth, + id, + inputRef, + isInvalid, + name, + placeholder, + resize = 'vertical', + rows, + ...rest + } = props; + const classes = classNames( 'euiTextArea', resizeToClassNameMap[resize], From f53807763581da355090dee39afb094b6e3c657d Mon Sep 17 00:00:00 2001 From: spalger Date: Fri, 9 Sep 2022 20:59:36 -0500 Subject: [PATCH 02/10] [docs] mention ability to specify fullWidth on EuiForm in the full width section --- .../form_layouts/form_layouts_example.js | 24 ++++++++++++------- src/components/form/form.tsx | 7 +++++- upcoming_changelogs/6229.md | 1 + 3 files changed, 23 insertions(+), 9 deletions(-) create mode 100644 upcoming_changelogs/6229.md diff --git a/src-docs/src/views/form_layouts/form_layouts_example.js b/src-docs/src/views/form_layouts/form_layouts_example.js index 2e697ecb05d..05e0a53fbf4 100644 --- a/src-docs/src/views/form_layouts/form_layouts_example.js +++ b/src-docs/src/views/form_layouts/form_layouts_example.js @@ -103,15 +103,23 @@ export const FormLayoutsExample = { `, }, { - title: 'Full-width', + title: 'Full-width (manual)', text: ( -

- Form elements will automatically flex to a max-width of{' '} - 400px. You can optionally pass the{' '} - fullWidth prop to the row and form control to - expand to their container. This should be done rarely and usually you - will only need it for isolated controls like search bars and sliders. -

+ <> +

+ Form elements will automatically flex to a max-width of{' '} + 400px. You can optionally pass the{' '} + fullWidth prop to the row and form control to + expand to their container. This should be done rarely and usually + you will only need it for isolated controls like search bars and + sliders. +

+

+ To set all the row and controls in a form to{' '} + fullWidth, specify the prop on the root{' '} + EuiForm component. +

+ ), props: { EuiFormRow, diff --git a/src/components/form/form.tsx b/src/components/form/form.tsx index ae101ff85d8..e7da31e41a1 100644 --- a/src/components/form/form.tsx +++ b/src/components/form/form.tsx @@ -35,7 +35,12 @@ export type EuiFormProps = CommonProps & * Where to display the callout with the list of errors */ invalidCallout?: 'above' | 'none'; - /** default `fullWidth` prop for children of this form */ + /** + * When set to `true`, all the rows/controls in this form will + * default to taking up 100% of the width of their continer. You + * can specify `fullWidth={false}` on individual rows/controls to + * disable this behavior for specific components. + */ fullWidth?: boolean; }; diff --git a/upcoming_changelogs/6229.md b/upcoming_changelogs/6229.md new file mode 100644 index 00000000000..4392d1524c4 --- /dev/null +++ b/upcoming_changelogs/6229.md @@ -0,0 +1 @@ +- Added support for `fullWidth` prop on EuiForm, which will be applied to all form elements rendered within From e590265b38be7dee6bf214a07c5423cb808a62f5 Mon Sep 17 00:00:00 2001 From: spalger Date: Fri, 9 Sep 2022 21:01:27 -0500 Subject: [PATCH 03/10] remove unnecessary text --- src-docs/src/views/form_layouts/form_layouts_example.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src-docs/src/views/form_layouts/form_layouts_example.js b/src-docs/src/views/form_layouts/form_layouts_example.js index 05e0a53fbf4..b66fd87d6d4 100644 --- a/src-docs/src/views/form_layouts/form_layouts_example.js +++ b/src-docs/src/views/form_layouts/form_layouts_example.js @@ -103,7 +103,7 @@ export const FormLayoutsExample = { `, }, { - title: 'Full-width (manual)', + title: 'Full-width', text: ( <>

From 737645068e1a111b67378bdf0b4b71793f8e0dee Mon Sep 17 00:00:00 2001 From: spalger Date: Fri, 9 Sep 2022 21:11:13 -0500 Subject: [PATCH 04/10] update changelog verbiage --- upcoming_changelogs/6229.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/upcoming_changelogs/6229.md b/upcoming_changelogs/6229.md index 4392d1524c4..b3383eddaed 100644 --- a/upcoming_changelogs/6229.md +++ b/upcoming_changelogs/6229.md @@ -1 +1 @@ -- Added support for `fullWidth` prop on EuiForm, which will be applied to all form elements rendered within +- Added support for `fullWidth` prop on EuiForm, which will be the default for all rows/controls within From fa97aa7a2a59453da3e174ef7de55d4a9e9fbdae Mon Sep 17 00:00:00 2001 From: spalger Date: Sat, 10 Sep 2022 04:12:21 +0000 Subject: [PATCH 05/10] define default values for docs which can't be determined by react-view anymore --- .../described_form_group/described_form_group.tsx | 4 ++++ src/components/form/field_number/field_number.tsx | 9 +++++++++ .../form/field_password/field_password.tsx | 6 ++++++ src/components/form/field_search/field_search.tsx | 5 +++++ src/components/form/field_text/field_text.tsx | 5 +++++ src/components/form/file_picker/file_picker.tsx | 5 +++++ src/components/form/form.tsx | 1 + .../form_control_layout/form_control_layout.tsx | 5 +++++ src/components/form/form_row/form_row.tsx | 5 +++++ src/components/form/range/dual_range.tsx | 5 +++++ src/components/form/range/range.tsx | 5 +++++ src/components/form/range/range_wrapper.tsx | 5 +++++ src/components/form/select/select.tsx | 10 ++++++++++ .../form/super_select/super_select_control.tsx | 14 ++++++++++++++ src/components/form/text_area/text_area.tsx | 6 ++++++ 15 files changed, 90 insertions(+) diff --git a/src/components/form/described_form_group/described_form_group.tsx b/src/components/form/described_form_group/described_form_group.tsx index a71635a637f..5546587adc4 100644 --- a/src/components/form/described_form_group/described_form_group.tsx +++ b/src/components/form/described_form_group/described_form_group.tsx @@ -30,18 +30,21 @@ export type EuiDescribedFormGroupProps = CommonProps & children?: ReactNode; /** * Passed to `EuiFlexGroup`. + * @default l */ gutterSize?: EuiFlexGroupGutterSize; /** * Expand to fill 100% of the parent. * Defaults to `fullWidth` prop of ``. * Default max-width is 800px. + * @default false */ fullWidth?: boolean; /** * Width ratio of description column compared to field column. * Can be used in conjunction with `fullWidth` and * may require `fullWidth` to be applied to child elements. + * @default half */ ratio?: 'half' | 'third' | 'quarter'; /** @@ -50,6 +53,7 @@ export type EuiDescribedFormGroupProps = CommonProps & title: EuiTitleProps['children']; /** * Adjust the visual `size` of the EuiTitle that wraps `title`. + * @default xs */ titleSize?: EuiTitleSize; /** diff --git a/src/components/form/field_number/field_number.tsx b/src/components/form/field_number/field_number.tsx index 07feafaed1c..777e287f379 100644 --- a/src/components/form/field_number/field_number.tsx +++ b/src/components/form/field_number/field_number.tsx @@ -28,7 +28,15 @@ export type EuiFieldNumberProps = Omit< CommonProps & { icon?: IconType; isInvalid?: boolean; + /** + * Expand to fill 100% of the parent. + * Defaults to `fullWidth` prop of ``. + * @default false + */ fullWidth?: boolean; + /** + * @default false + */ isLoading?: boolean; readOnly?: boolean; min?: number; @@ -61,6 +69,7 @@ export type EuiFieldNumberProps = Omit< /** * when `true` creates a shorter height input + * @default false */ compressed?: boolean; }; diff --git a/src/components/form/field_password/field_password.tsx b/src/components/form/field_password/field_password.tsx index 772e522de2e..461385f1558 100644 --- a/src/components/form/field_password/field_password.tsx +++ b/src/components/form/field_password/field_password.tsx @@ -33,6 +33,11 @@ export type EuiFieldPasswordProps = Omit< > & CommonProps & { isInvalid?: boolean; + /** + * Expand to fill 100% of the parent. + * Defaults to `fullWidth` prop of ``. + * @default false + */ fullWidth?: boolean; isLoading?: boolean; compressed?: boolean; @@ -55,6 +60,7 @@ export type EuiFieldPasswordProps = Omit< * Change the `type` of input for manually handling obfuscation. * The `dual` option adds the ability to toggle the obfuscation of the input by * adding an icon button as the first `append` element + * @default password */ type?: 'password' | 'text' | 'dual'; diff --git a/src/components/form/field_search/field_search.tsx b/src/components/form/field_search/field_search.tsx index 525e34b3741..b3c9bc595ef 100644 --- a/src/components/form/field_search/field_search.tsx +++ b/src/components/form/field_search/field_search.tsx @@ -29,6 +29,11 @@ export interface EuiFieldSearchProps placeholder?: string; value?: string; isInvalid?: boolean; + /** + * Expand to fill 100% of the parent. + * Defaults to `fullWidth` prop of ``. + * @default false + */ fullWidth?: boolean; isLoading?: boolean; /** diff --git a/src/components/form/field_text/field_text.tsx b/src/components/form/field_text/field_text.tsx index 1c6e6a04c01..5c75564de71 100644 --- a/src/components/form/field_text/field_text.tsx +++ b/src/components/form/field_text/field_text.tsx @@ -23,6 +23,11 @@ export type EuiFieldTextProps = InputHTMLAttributes & CommonProps & { icon?: EuiFormControlLayoutProps['icon']; isInvalid?: boolean; + /** + * Expand to fill 100% of the parent. + * Defaults to `fullWidth` prop of ``. + * @default false + */ fullWidth?: boolean; isLoading?: boolean; readOnly?: boolean; diff --git a/src/components/form/file_picker/file_picker.tsx b/src/components/form/file_picker/file_picker.tsx index 80c7f5efdd4..ea68af5751d 100644 --- a/src/components/form/file_picker/file_picker.tsx +++ b/src/components/form/file_picker/file_picker.tsx @@ -53,6 +53,11 @@ export interface EuiFilePickerProps * `large` for taller size */ display?: EuiFilePickerDisplay; + /** + * Expand to fill 100% of the parent. + * Defaults to `fullWidth` prop of ``. + * @default false + */ fullWidth?: boolean; isInvalid?: boolean; isLoading?: boolean; diff --git a/src/components/form/form.tsx b/src/components/form/form.tsx index e7da31e41a1..a3d653f72d1 100644 --- a/src/components/form/form.tsx +++ b/src/components/form/form.tsx @@ -40,6 +40,7 @@ export type EuiFormProps = CommonProps & * default to taking up 100% of the width of their continer. You * can specify `fullWidth={false}` on individual rows/controls to * disable this behavior for specific components. + * @default false */ fullWidth?: boolean; }; diff --git a/src/components/form/form_control_layout/form_control_layout.tsx b/src/components/form/form_control_layout/form_control_layout.tsx index 827a81cd628..c585be791ac 100644 --- a/src/components/form/form_control_layout/form_control_layout.tsx +++ b/src/components/form/form_control_layout/form_control_layout.tsx @@ -43,6 +43,11 @@ export type EuiFormControlLayoutProps = CommonProps & children?: ReactNode; icon?: EuiFormControlLayoutIconsProps['icon']; clear?: EuiFormControlLayoutIconsProps['clear']; + /** + * Expand to fill 100% of the parent. + * Defaults to `fullWidth` prop of ``. + * @default false + */ fullWidth?: boolean; isLoading?: boolean; isDisabled?: boolean; diff --git a/src/components/form/form_row/form_row.tsx b/src/components/form/form_row/form_row.tsx index fdf5b09ec43..e8b19680f9b 100644 --- a/src/components/form/form_row/form_row.tsx +++ b/src/components/form/form_row/form_row.tsx @@ -57,6 +57,11 @@ type EuiFormRowCommonProps = CommonProps & { */ display?: EuiFormRowDisplayKeys; hasEmptyLabelSpace?: boolean; + /** + * Expand to fill 100% of the parent. + * Defaults to `fullWidth` prop of ``. + * @default false + */ fullWidth?: boolean; /** * IDs of additional elements that should be part of children's `aria-describedby` diff --git a/src/components/form/range/dual_range.tsx b/src/components/form/range/dual_range.tsx index 16f955e5ecd..d986f6ad7fa 100644 --- a/src/components/form/range/dual_range.tsx +++ b/src/components/form/range/dual_range.tsx @@ -56,6 +56,11 @@ export interface EuiDualRangeProps | React.KeyboardEvent | React.KeyboardEvent ) => void; + /** + * Expand to fill 100% of the parent. + * Defaults to `fullWidth` prop of ``. + * @default false + */ fullWidth?: boolean; isInvalid?: boolean; /** diff --git a/src/components/form/range/range.tsx b/src/components/form/range/range.tsx index 2b49ee1810e..81f74f33f93 100644 --- a/src/components/form/range/range.tsx +++ b/src/components/form/range/range.tsx @@ -30,6 +30,11 @@ export interface EuiRangeProps Omit { compressed?: boolean; readOnly?: boolean; + /** + * Expand to fill 100% of the parent. + * Defaults to `fullWidth` prop of ``. + * @default false + */ fullWidth?: boolean; id?: string; /** diff --git a/src/components/form/range/range_wrapper.tsx b/src/components/form/range/range_wrapper.tsx index bf20a88bbe4..ceb7bf9f261 100644 --- a/src/components/form/range/range_wrapper.tsx +++ b/src/components/form/range/range_wrapper.tsx @@ -14,6 +14,11 @@ import { useFormContext } from '../eui_form_context'; export interface EuiRangeWrapperProps extends CommonProps, HTMLAttributes { + /** + * Expand to fill 100% of the parent. + * Defaults to `fullWidth` prop of ``. + * @default false + */ fullWidth?: boolean; compressed?: boolean; } diff --git a/src/components/form/select/select.tsx b/src/components/form/select/select.tsx index 1d280cd3a95..cd75d3fc560 100644 --- a/src/components/form/select/select.tsx +++ b/src/components/form/select/select.tsx @@ -32,13 +32,22 @@ export type EuiSelectProps = Omit< 'value' > & CommonProps & { + /** + * @default [] + */ options?: EuiSelectOption[]; isInvalid?: boolean; + /** + * Expand to fill 100% of the parent. + * Defaults to `fullWidth` prop of ``. + * @default false + */ fullWidth?: boolean; isLoading?: boolean; /** * Simulates no selection by creating an empty, selected, hidden first option + * @default false */ hasNoInitialSelection?: boolean; inputRef?: Ref; @@ -46,6 +55,7 @@ export type EuiSelectProps = Omit< /** * when `true` creates a shorter height input + * @default false */ compressed?: boolean; diff --git a/src/components/form/super_select/super_select_control.tsx b/src/components/form/super_select/super_select_control.tsx index 283b3d4e554..5f892b4f377 100644 --- a/src/components/form/super_select/super_select_control.tsx +++ b/src/components/form/super_select/super_select_control.tsx @@ -36,9 +36,23 @@ export interface EuiSuperSelectOption { export interface EuiSuperSelectControlProps extends CommonProps, Omit, 'value'> { + /** + * @default false + */ compressed?: boolean; + /** + * Expand to fill 100% of the parent. + * Defaults to `fullWidth` prop of ``. + * @default false + */ fullWidth?: boolean; + /** + * @default false + */ isInvalid?: boolean; + /** + * @default false + */ isLoading?: boolean; readOnly?: boolean; diff --git a/src/components/form/text_area/text_area.tsx b/src/components/form/text_area/text_area.tsx index 648c0bc25e8..e9ee50fa4be 100644 --- a/src/components/form/text_area/text_area.tsx +++ b/src/components/form/text_area/text_area.tsx @@ -15,11 +15,17 @@ import { useFormContext } from '../eui_form_context'; export type EuiTextAreaProps = TextareaHTMLAttributes & CommonProps & { isInvalid?: boolean; + /** + * Expand to fill 100% of the parent. + * Defaults to `fullWidth` prop of ``. + * @default false + */ fullWidth?: boolean; compressed?: boolean; /** * Which direction, if at all, should the textarea resize + * @default vertical */ resize?: keyof typeof resizeToClassNameMap; From 53950f9abe9b2905085021d8b258c809c94062e6 Mon Sep 17 00:00:00 2001 From: spalger Date: Tue, 27 Sep 2022 19:15:29 -0500 Subject: [PATCH 06/10] add dedicated docs example for fullWidth via context --- .../form_layouts/form_layouts_example.js | 50 ++++++++++++------ .../form_layouts/full_width_via_context.js | 51 +++++++++++++++++++ 2 files changed, 86 insertions(+), 15 deletions(-) create mode 100644 src-docs/src/views/form_layouts/full_width_via_context.js diff --git a/src-docs/src/views/form_layouts/form_layouts_example.js b/src-docs/src/views/form_layouts/form_layouts_example.js index b66fd87d6d4..c47ad96f90e 100644 --- a/src-docs/src/views/form_layouts/form_layouts_example.js +++ b/src-docs/src/views/form_layouts/form_layouts_example.js @@ -25,6 +25,9 @@ const describedFormGroupRatioSource = require('!!raw-loader!./described_form_gro import FullWidth from './full_width'; const fullWidthSource = require('!!raw-loader!./full_width'); +import FullWidthViaContext from './full_width_via_context'; +const fullWidthViaContextSource = require('!!raw-loader!./full_width_via_context'); + import Inline from './inline'; const inlineSource = require('!!raw-loader!./inline'); @@ -105,21 +108,13 @@ export const FormLayoutsExample = { { title: 'Full-width', text: ( - <> -

- Form elements will automatically flex to a max-width of{' '} - 400px. You can optionally pass the{' '} - fullWidth prop to the row and form control to - expand to their container. This should be done rarely and usually - you will only need it for isolated controls like search bars and - sliders. -

-

- To set all the row and controls in a form to{' '} - fullWidth, specify the prop on the root{' '} - EuiForm component. -

- +

+ Form elements will automatically flex to a max-width of{' '} + 400px. You can optionally pass the{' '} + fullWidth prop to the row and form control to + expand to their container. This should be done rarely and usually you + will only need it for isolated controls like search bars and sliders. +

), props: { EuiFormRow, @@ -138,6 +133,31 @@ export const FormLayoutsExample = { > `, + }, + { + title: 'Global Full-width', + text: ( +

+ To set all the row and controls in a form to{' '} + fullWidth, specify the prop on the root{' '} + EuiForm component. +

+ ), + props: { + EuiForm, + }, + demo: , + source: [ + { + type: GuideSectionTypes.JS, + code: fullWidthViaContextSource, + }, + ], + snippet: ` + + + +`, }, { title: 'Inline', diff --git a/src-docs/src/views/form_layouts/full_width_via_context.js b/src-docs/src/views/form_layouts/full_width_via_context.js new file mode 100644 index 00000000000..eb5af28f994 --- /dev/null +++ b/src-docs/src/views/form_layouts/full_width_via_context.js @@ -0,0 +1,51 @@ +import React from 'react'; + +import { + EuiForm, + EuiFieldSearch, + EuiRange, + EuiTextArea, + EuiFormRow, + EuiFlexGroup, + EuiFlexItem, + EuiSpacer, + EuiButton, +} from '../../../../src/components'; + +export default () => ( + { + e.preventDefault(); + }} + > + + + + + + Search + + + + + + + + + + + + + +); From 301afc4a7528ad755362661e45ab580a6d02a95f Mon Sep 17 00:00:00 2001 From: spalger Date: Wed, 28 Sep 2022 01:30:31 +0000 Subject: [PATCH 07/10] expand and customize the example a bit more --- .../form_layouts/full_width_via_context.js | 51 --------- .../form_layouts/full_width_via_context.tsx | 101 ++++++++++++++++++ 2 files changed, 101 insertions(+), 51 deletions(-) delete mode 100644 src-docs/src/views/form_layouts/full_width_via_context.js create mode 100644 src-docs/src/views/form_layouts/full_width_via_context.tsx diff --git a/src-docs/src/views/form_layouts/full_width_via_context.js b/src-docs/src/views/form_layouts/full_width_via_context.js deleted file mode 100644 index eb5af28f994..00000000000 --- a/src-docs/src/views/form_layouts/full_width_via_context.js +++ /dev/null @@ -1,51 +0,0 @@ -import React from 'react'; - -import { - EuiForm, - EuiFieldSearch, - EuiRange, - EuiTextArea, - EuiFormRow, - EuiFlexGroup, - EuiFlexItem, - EuiSpacer, - EuiButton, -} from '../../../../src/components'; - -export default () => ( - { - e.preventDefault(); - }} - > - - - - - - Search - - - - - - - - - - - - - -); diff --git a/src-docs/src/views/form_layouts/full_width_via_context.tsx b/src-docs/src/views/form_layouts/full_width_via_context.tsx new file mode 100644 index 00000000000..8974b9a088a --- /dev/null +++ b/src-docs/src/views/form_layouts/full_width_via_context.tsx @@ -0,0 +1,101 @@ +import React from 'react'; + +import { + EuiForm, + EuiFieldSearch, + EuiRange, + EuiTextArea, + EuiFormRow, + EuiFlexGroup, + EuiFlexItem, + EuiSpacer, + EuiDescribedFormGroup, + EuiSelect, + EuiFilePicker, + EuiButton, +} from '../../../../src/components'; + +export default () => { + const [range, setRange] = React.useState(42); + + return ( + { + e.preventDefault(); + }} + > + + + + + + Search + + + + + + + { + if (e.target instanceof HTMLInputElement) { + setRange(Number.parseInt(e.target.value, 10)); + } + }} + /> + + + + + Works with all form controls and layout components} + description={ + <> +

+ Any component that supports the fullWidth prop that + is. +

+

+ Make sure it is appropriate at all of the widths that the + container can take. There are many situations where a full-width + form is inappropriate. +

+ + } + > + + + +
+ + + + + + + + +
+ ); +}; From bca771ec5c023f308c587d1e17fe1b67818352b6 Mon Sep 17 00:00:00 2001 From: spalger Date: Wed, 28 Sep 2022 14:01:01 +0000 Subject: [PATCH 08/10] remove snapshot test, just validate fullWidth class exists --- .../__snapshots__/dual_range.test.tsx.snap | 34 ------------------- src/components/form/range/dual_range.test.tsx | 10 +++++- 2 files changed, 9 insertions(+), 35 deletions(-) diff --git a/src/components/form/range/__snapshots__/dual_range.test.tsx.snap b/src/components/form/range/__snapshots__/dual_range.test.tsx.snap index c2013a095ce..62be3a9f356 100644 --- a/src/components/form/range/__snapshots__/dual_range.test.tsx.snap +++ b/src/components/form/range/__snapshots__/dual_range.test.tsx.snap @@ -52,40 +52,6 @@ exports[`EuiDualRange allows value prop to accept numbers 1`] = `
`; -exports[`EuiDualRange inherits fullWidth from 1`] = ` -
-
-
- -
-
-
-
-
-
-`; - exports[`EuiDualRange input props can be applied to min and max inputs 1`] = `
{ ); - expect(component).toMatchSnapshot(); + if ( + !component + .find('.euiRangeWrapper') + .hasClass('euiRangeWrapper--fullWidth') + ) { + throw new Error( + 'expected EuiDualRange to inherit fullWidth from EuiForm' + ); + } }); }); }); From 303968c434b7934c22238551474f66ea9a991c17 Mon Sep 17 00:00:00 2001 From: spalger Date: Wed, 28 Sep 2022 14:04:19 +0000 Subject: [PATCH 09/10] add example of fullWidth being disabled in context --- src-docs/src/views/form_layouts/full_width_via_context.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src-docs/src/views/form_layouts/full_width_via_context.tsx b/src-docs/src/views/form_layouts/full_width_via_context.tsx index 8974b9a088a..ba85b140bd3 100644 --- a/src-docs/src/views/form_layouts/full_width_via_context.tsx +++ b/src-docs/src/views/form_layouts/full_width_via_context.tsx @@ -93,8 +93,8 @@ export default () => { /> - - + + ); From 814de11a80dea085cd557e427e40044d3cbcde47 Mon Sep 17 00:00:00 2001 From: Constance Date: Wed, 28 Sep 2022 08:17:59 -0700 Subject: [PATCH 10/10] [PR feedback] Docs sentence casing --- src-docs/src/views/form_layouts/form_layouts_example.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src-docs/src/views/form_layouts/form_layouts_example.js b/src-docs/src/views/form_layouts/form_layouts_example.js index c47ad96f90e..61e1df7912b 100644 --- a/src-docs/src/views/form_layouts/form_layouts_example.js +++ b/src-docs/src/views/form_layouts/form_layouts_example.js @@ -135,7 +135,7 @@ export const FormLayoutsExample = { `, }, { - title: 'Global Full-width', + title: 'Global full-width', text: (

To set all the row and controls in a form to{' '}