Skip to content

Commit dccc1f4

Browse files
fix: Misaligned 'Remove' button when no label is present in the attribute editor (cloudscape-design#528)
1 parent d815129 commit dccc1f4

File tree

3 files changed

+96
-4
lines changed

3 files changed

+96
-4
lines changed

Diff for: pages/attribute-editor/form-field-label.page.tsx

+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
import React, { useState, useCallback, useMemo } from 'react';
4+
import AttributeEditor, { AttributeEditorProps } from '~components/attribute-editor';
5+
import { Box, FormField, Input, InputProps, NonCancelableCustomEvent } from '~components';
6+
7+
interface Tag {
8+
key?: string;
9+
value?: string;
10+
}
11+
12+
interface ControlProps extends InputProps {
13+
index: number;
14+
setItems?: any;
15+
prop: keyof Tag;
16+
}
17+
18+
const i18nStrings = {
19+
addButtonText: 'Add new user',
20+
removeButtonText: 'Remove',
21+
empty: 'No secondary owners assigned to this resource.',
22+
};
23+
24+
const Control = React.memo(({ value, index, setItems, prop }: ControlProps) => {
25+
return (
26+
<Input
27+
value={value}
28+
ariaLabel="Secondary owner username"
29+
ariaLabelledby=""
30+
onChange={({ detail }) => {
31+
setItems((items: any) => {
32+
const updatedItems = [...items];
33+
updatedItems[index] = { ...updatedItems[index], [prop]: detail.value };
34+
return updatedItems;
35+
});
36+
}}
37+
/>
38+
);
39+
});
40+
41+
export default function AttributeEditorPage() {
42+
const [items, setItems] = useState<Tag[]>([{ key: '' }]);
43+
44+
const definition: AttributeEditorProps.FieldDefinition<Tag>[] = useMemo(
45+
() => [
46+
{
47+
control: ({ key = '' }, itemIndex) => <Control prop="key" value={key} index={itemIndex} setItems={setItems} />,
48+
},
49+
],
50+
[]
51+
);
52+
53+
const onAddButtonClick = useCallback(() => {
54+
setItems(items => [...items, {}]);
55+
}, []);
56+
57+
const onRemoveButtonClick = useCallback(
58+
({ detail: { itemIndex } }: NonCancelableCustomEvent<AttributeEditorProps.RemoveButtonClickDetail>) => {
59+
setItems(items => {
60+
const newItems = items.slice();
61+
newItems.splice(itemIndex, 1);
62+
return newItems;
63+
});
64+
},
65+
[]
66+
);
67+
68+
return (
69+
<Box margin="xl">
70+
<h1>Attribute Editor - Using a form field label</h1>
71+
<FormField label="Secondary owners" description="Secondary owners can edit this profile.">
72+
<AttributeEditor
73+
{...i18nStrings}
74+
items={items}
75+
definition={definition}
76+
onAddButtonClick={onAddButtonClick}
77+
onRemoveButtonClick={onRemoveButtonClick}
78+
/>
79+
</FormField>
80+
</Box>
81+
);
82+
}

Diff for: src/attribute-editor/row.tsx

+9-3
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,11 @@ export const Row = React.memo(
8888
))}
8989
</InternalColumnLayout>
9090
{removable && (
91-
<ButtonContainer index={index} isNarrowViewport={isNarrowViewport}>
91+
<ButtonContainer
92+
index={index}
93+
isNarrowViewport={isNarrowViewport}
94+
hasLabel={definition.some(row => row.label)}
95+
>
9296
<InternalButton
9397
className={styles['remove-button']}
9498
formAction="none"
@@ -113,12 +117,14 @@ interface ButtonContainer {
113117
index: number;
114118
children: React.ReactNode;
115119
isNarrowViewport: boolean;
120+
hasLabel: boolean;
116121
}
117122

118-
const ButtonContainer = ({ index, children, isNarrowViewport }: ButtonContainer) => (
123+
const ButtonContainer = ({ index, children, isNarrowViewport, hasLabel }: ButtonContainer) => (
119124
<div
120125
className={clsx({
121-
[styles['button-container']]: !isNarrowViewport && index === 0,
126+
[styles['button-container-haslabel']]: !isNarrowViewport && index === 0 && hasLabel,
127+
[styles['button-container-nolabel']]: !isNarrowViewport && index === 0 && !hasLabel,
122128
[styles['right-align']]: isNarrowViewport,
123129
})}
124130
>

Diff for: src/attribute-editor/styles.scss

+5-1
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,18 @@
3737
/* used in test-utils */
3838
}
3939

40-
.button-container {
40+
.button-container-haslabel {
4141
// We only support vertical alignment of the remove button for labels with exactly one line.
4242
// The value is calculated as follows:
4343
// padding-top = awsui-form-field-controls: 4px +
4444
// line height (also applies to icon size) awsui-form-field-label: 22px
4545
padding-top: calc(#{awsui.$space-xxs} + #{awsui.$font-body-m-line-height});
4646
}
4747

48+
.button-container-nolabel {
49+
padding-top: #{awsui.$space-xxs};
50+
}
51+
4852
.divider {
4953
border-bottom: awsui.$border-divider-section-width solid awsui.$color-border-divider-default;
5054
}

0 commit comments

Comments
 (0)