Skip to content

Commit

Permalink
Merge pull request #3 from varghvi/main
Browse files Browse the repository at this point in the history
sync
  • Loading branch information
varghvi authored Nov 29, 2022
2 parents 727032a + cc4db96 commit 22520b2
Show file tree
Hide file tree
Showing 23 changed files with 609 additions and 119 deletions.
82 changes: 82 additions & 0 deletions pages/attribute-editor/form-field-label.page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import React, { useState, useCallback, useMemo } from 'react';
import AttributeEditor, { AttributeEditorProps } from '~components/attribute-editor';
import { Box, FormField, Input, InputProps, NonCancelableCustomEvent } from '~components';

interface Tag {
key?: string;
value?: string;
}

interface ControlProps extends InputProps {
index: number;
setItems?: any;
prop: keyof Tag;
}

const i18nStrings = {
addButtonText: 'Add new user',
removeButtonText: 'Remove',
empty: 'No secondary owners assigned to this resource.',
};

const Control = React.memo(({ value, index, setItems, prop }: ControlProps) => {
return (
<Input
value={value}
ariaLabel="Secondary owner username"
ariaLabelledby=""
onChange={({ detail }) => {
setItems((items: any) => {
const updatedItems = [...items];
updatedItems[index] = { ...updatedItems[index], [prop]: detail.value };
return updatedItems;
});
}}
/>
);
});

export default function AttributeEditorPage() {
const [items, setItems] = useState<Tag[]>([{ key: '' }]);

const definition: AttributeEditorProps.FieldDefinition<Tag>[] = useMemo(
() => [
{
control: ({ key = '' }, itemIndex) => <Control prop="key" value={key} index={itemIndex} setItems={setItems} />,
},
],
[]
);

const onAddButtonClick = useCallback(() => {
setItems(items => [...items, {}]);
}, []);

const onRemoveButtonClick = useCallback(
({ detail: { itemIndex } }: NonCancelableCustomEvent<AttributeEditorProps.RemoveButtonClickDetail>) => {
setItems(items => {
const newItems = items.slice();
newItems.splice(itemIndex, 1);
return newItems;
});
},
[]
);

return (
<Box margin="xl">
<h1>Attribute Editor - Using a form field label</h1>
<FormField label="Secondary owners" description="Secondary owners can edit this profile.">
<AttributeEditor
{...i18nStrings}
items={items}
definition={definition}
onAddButtonClick={onAddButtonClick}
onRemoveButtonClick={onRemoveButtonClick}
/>
</FormField>
</Box>
);
}
48 changes: 37 additions & 11 deletions pages/breadcrumb-group/events.page.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import React, { useState } from 'react';
import { SpaceBetween } from '~components';

import BreadcrumbGroup, { BreadcrumbGroupProps } from '~components/breadcrumb-group';
import ScreenshotArea from '../utils/screenshot-area';
const items = ['First', 'Second', 'Third', 'Fourth', 'Fifth', 'Sixth'];
const items = [
'First that is very very very very very very long long long text',
'Second',
'Third',
'Fourth',
'Fifth',
'Sixth that is very very very very very very long long long text',
];

const shortItems = ['1', '2', '3', '4'];

export default function ButtonDropdownPage() {
const [onFollowMessage, setOnFollowMessage] = useState('');
Expand All @@ -20,16 +30,32 @@ export default function ButtonDropdownPage() {
<ScreenshotArea disableAnimations={true}>
<article>
<h1>BreadcrumbGroup variations</h1>
<BreadcrumbGroup
ariaLabel="Navigation"
expandAriaLabel="Show path"
items={items.map(text => ({ text, href: `#` }))}
onFollow={onFollowCallback}
onClick={onClickCallback}
/>
<div />
<div id="onFollowMessage">{onFollowMessage}</div>
<div id="onClickMessage">{onClickMessage}</div>
<SpaceBetween size="xxl">
<div>
<button type="button" id="focus-target-long-text">
focus long text
</button>
<BreadcrumbGroup
ariaLabel="Navigation long text"
expandAriaLabel="Show path for long text"
items={items.map(text => ({ text, href: `#` }))}
onFollow={onFollowCallback}
onClick={onClickCallback}
/>
<div id="onFollowMessage">{onFollowMessage}</div>
<div id="onClickMessage">{onClickMessage}</div>
</div>
<div>
<button type="button" id="focus-target-short-text">
focus short text
</button>
<BreadcrumbGroup
ariaLabel="Navigation short text"
expandAriaLabel="Show path for short text"
items={shortItems.map(text => ({ text, href: `#` }))}
/>
</div>
</SpaceBetween>
</article>
</ScreenshotArea>
);
Expand Down
17 changes: 12 additions & 5 deletions pages/date-range-picker/calendar-permutations.page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,26 @@ const intervals = [
['2021-05-10', '2021-05-30'],
];

const permutations = createPermutations<DateRangePickerCalendarProps>(
intervals.map(([startDate, endDate]) => ({
const permutations = createPermutations<DateRangePickerCalendarProps>([
...intervals.map(([startDate, endDate]) => ({
value: [{ start: { date: startDate, time: '' }, end: { date: endDate, time: '' } }],
setValue: [() => {}],
locale: ['en-GB'],
startOfWeek: [1],
isDateEnabled: [() => true],
onChange: [() => {}],
timeInputFormat: ['hh:mm:ss'],
timeInputFormat: ['hh:mm:ss'] as const,
i18nStrings: [i18nStrings],
dateOnly: [false, true],
}))
);
customAbsoluteRangeControl: [undefined],
})),
{
value: [{ start: { date: '', time: '' }, end: { date: '', time: '' } }],
setValue: [() => {}],
i18nStrings: [i18nStrings],
customAbsoluteRangeControl: [() => 'Custom control'],
},
]);

export default function DateRangePickerCalendarPage() {
let i = -1;
Expand Down
86 changes: 86 additions & 0 deletions pages/date-range-picker/custom-control.page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import React, { useState } from 'react';
import startOfWeek from 'date-fns/startOfWeek';
import endOfWeek from 'date-fns/endOfWeek';
import startOfMonth from 'date-fns/startOfMonth';
import endOfMonth from 'date-fns/endOfMonth';
import enLocale from 'date-fns/locale/en-GB';
import { Box, DateRangePicker, DateRangePickerProps, Link, FormField } from '~components';
import { i18nStrings, isValid } from './common';
import { formatDate } from '~components/internal/utils/date-time';

export default function DatePickerScenario() {
const [value, setValue] = useState<DateRangePickerProps['value']>(null);

return (
<Box padding="s">
<h1>Date range picker with custom control</h1>
<FormField label="Date Range Picker field">
<DateRangePicker
value={value}
onChange={e => setValue(e.detail.value)}
locale={enLocale.code}
i18nStrings={i18nStrings}
relativeOptions={[]}
placeholder="Filter by a date and time range"
isValidRange={isValid}
rangeSelectorMode="absolute-only"
customAbsoluteRangeControl={(selectedDate, setSelectedDate) => (
<>
Auto-select:{' '}
<Link
onFollow={() => {
const today = formatDate(new Date());
return setSelectedDate({
start: { date: today, time: '' },
end: { date: today, time: '' },
});
}}
>
1D
</Link>{' '}
<Link
variant="secondary"
onFollow={() =>
setSelectedDate({
start: {
date: formatDate(startOfWeek(new Date(), { locale: enLocale })),
time: '',
},
end: {
date: formatDate(endOfWeek(new Date(), { locale: enLocale })),
time: '',
},
})
}
>
7D
</Link>{' '}
<Link
onFollow={() =>
setSelectedDate({
start: { date: formatDate(startOfMonth(new Date())), time: '' },
end: { date: formatDate(endOfMonth(new Date())), time: '' },
})
}
>
1M
</Link>{' '}
<Link
onFollow={() =>
setSelectedDate({
start: { date: '', time: '' },
end: { date: '', time: '' },
})
}
>
None
</Link>
</>
)}
/>
</FormField>
</Box>
);
}
1 change: 1 addition & 0 deletions pages/date-range-picker/range-calendar.page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export default function RangeCalendarScenario() {
dateOnly={dateOnly}
timeInputFormat="hh:mm"
isDateEnabled={date => date.getDate() !== 15}
customAbsoluteRangeControl={undefined}
/>

<Link id="focusable-after">Focusable element after the range calendar</Link>
Expand Down
21 changes: 21 additions & 0 deletions src/__tests__/__snapshots__/documenter.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -4843,6 +4843,27 @@ is provided by its parent form field component.
"optional": true,
"type": "string",
},
Object {
"description": "Specifies an additional control displayed in the dropdown, located below the range calendar.",
"inlineType": Object {
"name": "DateRangePickerProps.AbsoluteRangeControl",
"parameters": Array [
Object {
"name": "selectedRange",
"type": "DateRangePickerProps.PendingAbsoluteValue",
},
Object {
"name": "setSelectedRange",
"type": "React.Dispatch<React.SetStateAction<DateRangePickerProps.PendingAbsoluteValue>>",
},
],
"returnType": "React.ReactNode",
"type": "function",
},
"name": "customAbsoluteRangeControl",
"optional": true,
"type": "DateRangePickerProps.AbsoluteRangeControl",
},
Object {
"defaultValue": "false",
"description": "Hides time inputs and changes the input format to date-only, e.g. 2021-04-06.
Expand Down
12 changes: 9 additions & 3 deletions src/attribute-editor/row.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,11 @@ export const Row = React.memo(
))}
</InternalColumnLayout>
{removable && (
<ButtonContainer index={index} isNarrowViewport={isNarrowViewport}>
<ButtonContainer
index={index}
isNarrowViewport={isNarrowViewport}
hasLabel={definition.some(row => row.label)}
>
<InternalButton
className={styles['remove-button']}
formAction="none"
Expand All @@ -113,12 +117,14 @@ interface ButtonContainer {
index: number;
children: React.ReactNode;
isNarrowViewport: boolean;
hasLabel: boolean;
}

const ButtonContainer = ({ index, children, isNarrowViewport }: ButtonContainer) => (
const ButtonContainer = ({ index, children, isNarrowViewport, hasLabel }: ButtonContainer) => (
<div
className={clsx({
[styles['button-container']]: !isNarrowViewport && index === 0,
[styles['button-container-haslabel']]: !isNarrowViewport && index === 0 && hasLabel,
[styles['button-container-nolabel']]: !isNarrowViewport && index === 0 && !hasLabel,
[styles['right-align']]: isNarrowViewport,
})}
>
Expand Down
6 changes: 5 additions & 1 deletion src/attribute-editor/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,18 @@
/* used in test-utils */
}

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

.button-container-nolabel {
padding-top: #{awsui.$space-xxs};
}

.divider {
border-bottom: awsui.$border-divider-section-width solid awsui.$color-border-divider-default;
}
Expand Down
34 changes: 34 additions & 0 deletions src/breadcrumb-group/__integ__/breadcrumb-group.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import useBrowser from '@cloudscape-design/browser-test-tools/use-browser';
import createWrapper from '../../../lib/components/test-utils/selectors';
import { BasePageObject } from '@cloudscape-design/browser-test-tools/page-objects';
import styles from '../../../lib/components/breadcrumb-group/item/styles.selectors.js';

const breadcrumbGroupWrapper = createWrapper().findBreadcrumbGroup();
const dropdownWrapper = breadcrumbGroupWrapper.findDropdown();
Expand Down Expand Up @@ -73,4 +74,37 @@ describe('BreadcrumbGroup', () => {
await expect(page.getText('#onClickMessage')).resolves.toEqual('OnClick: Second item was selected');
})
);

test(
'Item popover should not show on large screen',
setupTest(async page => {
await page.setWindowSize({ width: 1200, height: 800 });
await page.click('#focus-target-long-text');
await page.keys('Tab');
await expect(page.isExisting(createWrapper().find(`.${styles['item-popover']}`).toSelector())).resolves.toBe(
false
);
})
);

test(
'Item popover should show on small screen when text get truncated, and should close pressing Escape',
setupTest(async page => {
await page.setMobileViewport();
await page.click('#focus-target-long-text');
await page.keys('Tab');
await expect(page.isExisting(createWrapper().find(`.${styles['item-popover']}`).toSelector())).resolves.toBe(
true
);
await page.keys('Escape');
await expect(page.isExisting(createWrapper().find(`.${styles['item-popover']}`).toSelector())).resolves.toBe(
false
);
await page.click('#focus-target-short-text');
await page.keys('Tab');
await expect(page.isExisting(createWrapper().find(`.${styles['item-popover']}`).toSelector())).resolves.toBe(
false
);
})
);
});
Loading

0 comments on commit 22520b2

Please sign in to comment.