Skip to content

Commit

Permalink
Implemented design feedback
Browse files Browse the repository at this point in the history
- decreased spacing between list items and button
- fixed a11y issue between label and first text field
- moved help text to under label
- refactored all of the field layout logic into drag and drop
  text list component.
  • Loading branch information
jloleysens committed Sep 18, 2020
1 parent f5c707b commit c4235f9
Show file tree
Hide file tree
Showing 3 changed files with 151 additions and 124 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
.pipelineProcessorsEditor__form__dragAndDropList {
&__panel {
box-shadow: none;
background-color: $euiColorLightestShade;
padding: $euiSizeM;
}

&__grabIcon {
Expand All @@ -16,9 +17,12 @@
}

&__item {
background-color: $euiColorEmptyShade;
padding-top: $euiSizeM;
padding-bottom: $euiSizeM;
border-bottom: $euiBorderThin;
background-color: $euiColorLightestShade;
padding-top: $euiSizeS;
padding-bottom: $euiSizeS;
}

&__labelContainer {
margin-bottom: $euiSizeXS;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ import {
EuiFlexGroup,
EuiFlexItem,
EuiIcon,
EuiPanel,
EuiSpacer,
EuiFieldText,
EuiIconTip,
EuiFormRow,
EuiText,
} from '@elastic/eui';

import {
Expand All @@ -32,6 +32,9 @@ import {
import './drag_and_drop_text_list.scss';

interface Props {
label: string;
helpText: React.ReactNode;
error: string | null;
value: ArrayItem[];
onMove: (sourceIdx: number, destinationIdx: number) => void;
onAdd: () => void;
Expand All @@ -51,6 +54,9 @@ const i18nTexts = {
};

function DragAndDropTextListComponent({
label,
helpText,
error,
value,
onMove,
onAdd,
Expand All @@ -59,6 +65,7 @@ function DragAndDropTextListComponent({
textValidation,
}: Props): JSX.Element {
const [droppableId] = useState(() => uuid.v4());
const [firstItemId] = useState(() => uuid.v4());

const onDragEnd = useCallback(
({ source, destination }) => {
Expand All @@ -69,110 +76,132 @@ function DragAndDropTextListComponent({
[onMove]
);
return (
<EuiPanel
className="pipelineProcessorsEditor__form__dragAndDropList__panel"
hasShadow={false}
paddingSize="s"
>
<EuiDragDropContext onDragEnd={onDragEnd}>
<EuiDroppable droppableId={droppableId}>
{value.map((item, idx) => {
return (
<EuiDraggable
customDragHandle
spacing="none"
draggableId={String(item.id)}
index={idx}
key={item.id}
>
{(provided) => {
return (
<EuiFlexGroup
className="pipelineProcessorsEditor__form__dragAndDropList__item"
justifyContent="center"
alignItems="center"
gutterSize="none"
>
<EuiFlexItem grow={false}>
<div {...provided.dragHandleProps}>
<EuiIcon
className="pipelineProcessorsEditor__form__dragAndDropList__grabIcon"
type="grab"
/>
</div>
</EuiFlexItem>
<EuiFlexItem>
<UseField<string>
path={item.path}
config={{
validations: textValidation
? [{ validator: textValidation }]
: undefined,
}}
readDefaultValueOnForm={!item.isNew}
<EuiFormRow isInvalid={typeof error === 'string'} error={error} fullWidth>
<>
{/* Label and help text. Also wire up the htmlFor so the label points to the first text field. */}
<EuiFlexGroup
className="pipelineProcessorsEditor__form__dragAndDropList__labelContainer"
justifyContent="flexStart"
direction="column"
gutterSize="none"
>
<EuiFlexItem grow={false}>
<EuiText size="xs">
<label htmlFor={firstItemId}>
<strong>{label}</strong>
</label>
</EuiText>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiText size="xs" color="subdued">
<p>{helpText}</p>
</EuiText>
</EuiFlexItem>
</EuiFlexGroup>

{/* The processor panel */}
<div className="pipelineProcessorsEditor__form__dragAndDropList__panel">
<EuiDragDropContext onDragEnd={onDragEnd}>
<EuiDroppable droppableId={droppableId}>
{value.map((item, idx) => {
return (
<EuiDraggable
customDragHandle
spacing="none"
draggableId={String(item.id)}
index={idx}
key={item.id}
>
{(provided) => {
return (
<EuiFlexGroup
className="pipelineProcessorsEditor__form__dragAndDropList__item"
justifyContent="center"
alignItems="center"
gutterSize="none"
>
{(field) => {
const { isInvalid, errorMessage } = getFieldValidityAndErrorMessage(
field
);
return (
<EuiFlexGroup gutterSize="none" alignItems="center">
<EuiFlexItem>
<EuiFieldText
isInvalid={isInvalid}
value={field.value}
onChange={field.onChange}
compressed
fullWidth
/>
</EuiFlexItem>
{typeof errorMessage === 'string' && (
<EuiFlexItem grow={false}>
<div className="pipelineProcessorsEditor__form__dragAndDropList__errorIcon">
<EuiIconTip
aria-label={errorMessage}
content={errorMessage}
type="alert"
color="danger"
<EuiFlexItem grow={false}>
<div {...provided.dragHandleProps}>
<EuiIcon
className="pipelineProcessorsEditor__form__dragAndDropList__grabIcon"
type="grab"
/>
</div>
</EuiFlexItem>
<EuiFlexItem>
<UseField<string>
path={item.path}
config={{
validations: textValidation
? [{ validator: textValidation }]
: undefined,
}}
readDefaultValueOnForm={!item.isNew}
>
{(field) => {
const { isInvalid, errorMessage } = getFieldValidityAndErrorMessage(
field
);
return (
<EuiFlexGroup gutterSize="none" alignItems="center">
<EuiFlexItem>
<EuiFieldText
id={idx === 0 ? firstItemId : undefined}
isInvalid={isInvalid}
value={field.value}
onChange={field.onChange}
compressed
fullWidth
/>
</div>
</EuiFlexItem>
)}
</EuiFlexGroup>
);
}}
</UseField>
</EuiFlexItem>
<EuiFlexItem grow={false}>
{value.length > 1 ? (
<EuiButtonIcon
aria-label={i18nTexts.removeItemButtonAriaLabel}
className="pipelineProcessorsEditor__form__dragAndDropList__removeButton"
iconType="minusInCircle"
color="danger"
onClick={() => onRemove(item.id)}
/>
) : (
// Render a no-op placeholder button
<EuiIcon
className="pipelineProcessorsEditor__form__dragAndDropList__removeButton"
type="empty"
/>
)}
</EuiFlexItem>
</EuiFlexGroup>
);
}}
</EuiDraggable>
);
})}
</EuiDroppable>
</EuiDragDropContext>
{value.length ? <EuiSpacer size="s" /> : null}
<EuiButtonEmpty iconType="plusInCircle" onClick={onAdd}>
{addLabel}
</EuiButtonEmpty>
</EuiPanel>
</EuiFlexItem>
{typeof errorMessage === 'string' && (
<EuiFlexItem grow={false}>
<div className="pipelineProcessorsEditor__form__dragAndDropList__errorIcon">
<EuiIconTip
aria-label={errorMessage}
content={errorMessage}
type="alert"
color="danger"
/>
</div>
</EuiFlexItem>
)}
</EuiFlexGroup>
);
}}
</UseField>
</EuiFlexItem>
<EuiFlexItem grow={false}>
{value.length > 1 ? (
<EuiButtonIcon
aria-label={i18nTexts.removeItemButtonAriaLabel}
className="pipelineProcessorsEditor__form__dragAndDropList__removeButton"
iconType="minusInCircle"
color="danger"
onClick={() => onRemove(item.id)}
/>
) : (
// Render a no-op placeholder button
<EuiIcon
className="pipelineProcessorsEditor__form__dragAndDropList__removeButton"
type="empty"
/>
)}
</EuiFlexItem>
</EuiFlexGroup>
);
}}
</EuiDraggable>
);
})}
</EuiDroppable>
</EuiDragDropContext>
<EuiButtonEmpty iconType="plusInCircle" onClick={onAdd}>
{addLabel}
</EuiButtonEmpty>
</div>
</>
</EuiFormRow>
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

import React, { FunctionComponent } from 'react';
import { i18n } from '@kbn/i18n';
import { EuiFormRow } from '@elastic/eui';

import {
FIELD_TYPES,
Expand Down Expand Up @@ -124,22 +123,17 @@ export const Grok: FunctionComponent = () => {
<UseArray path="fields.patterns" validations={fieldsConfig.patterns.validations}>
{({ items, addItem, removeItem, moveItem, error }) => {
return (
<EuiFormRow
label={fieldsConfig.patterns.label}
<DragAndDropTextList
label={fieldsConfig.patterns.label!}
helpText={fieldsConfig.patterns.helpText}
isInvalid={typeof error === 'string'}
error={error}
fullWidth
>
<DragAndDropTextList
value={items}
onMove={moveItem}
onAdd={addItem}
onRemove={removeItem}
addLabel={i18nTexts.addPatternLabel}
textValidation={patternValidation}
/>
</EuiFormRow>
value={items}
onMove={moveItem}
onAdd={addItem}
onRemove={removeItem}
addLabel={i18nTexts.addPatternLabel}
textValidation={patternValidation}
/>
);
}}
</UseArray>
Expand Down

0 comments on commit c4235f9

Please sign in to comment.